1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import {
- CommandToolbarButton,
- createToolbarFactory,
- ReactiveToolbar,
- SessionContext,
- Toolbar,
- ToolbarButton,
- ToolbarRegistry,
- ToolbarWidgetRegistry
- } from '@jupyterlab/apputils';
- import { ISettingRegistry, SettingRegistry } from '@jupyterlab/settingregistry';
- import { IDataConnector } from '@jupyterlab/statedb';
- import {
- createSessionContext,
- framePromise,
- JupyterServer
- } from '@jupyterlab/testutils';
- import { ITranslator } from '@jupyterlab/translation';
- import {
- blankIcon,
- bugDotIcon,
- bugIcon,
- jupyterIcon
- } from '@jupyterlab/ui-components';
- import { toArray } from '@lumino/algorithm';
- import { CommandRegistry } from '@lumino/commands';
- import {
- JSONExt,
- PromiseDelegate,
- ReadonlyPartialJSONObject
- } from '@lumino/coreutils';
- import { PanelLayout, Widget } from '@lumino/widgets';
- import { simulate } from 'simulate-event';
- const server = new JupyterServer();
- beforeAll(async () => {
- await server.start();
- });
- afterAll(async () => {
- await server.shutdown();
- });
- describe('@jupyterlab/apputils', () => {
- describe('CommandToolbarButton', () => {
- let commands: CommandRegistry;
- const id = 'test-command';
- const options: CommandRegistry.ICommandOptions = {
- execute: jest.fn()
- };
- beforeEach(() => {
- commands = new CommandRegistry();
- });
- it('should render a command', async () => {
- commands.addCommand(id, options);
- const button = new CommandToolbarButton({
- commands,
- id
- });
- Widget.attach(button, document.body);
- await framePromise();
- expect(button.hasClass('jp-CommandToolbarButton')).toBe(true);
- simulate(button.node.firstElementChild!, 'mousedown');
- expect(options.execute).toBeCalledTimes(1);
- });
- it('should render the label command', async () => {
- const label = 'This is a test label';
- commands.addCommand(id, { ...options, label });
- const button = new CommandToolbarButton({
- commands,
- id
- });
- Widget.attach(button, document.body);
- await framePromise();
- expect(button.node.textContent).toMatch(label);
- });
- it('should render the customized label command', async () => {
- const label = 'This is a test label';
- const buttonLabel = 'This is the button label';
- commands.addCommand(id, { ...options, label });
- const button = new CommandToolbarButton({
- commands,
- id,
- label: buttonLabel
- });
- Widget.attach(button, document.body);
- await framePromise();
- expect(button.node.textContent).toMatch(buttonLabel);
- expect(button.node.textContent).not.toMatch(label);
- });
- it('should render the icon command', async () => {
- const icon = jupyterIcon;
- commands.addCommand(id, { ...options, icon });
- const button = new CommandToolbarButton({
- commands,
- id
- });
- Widget.attach(button, document.body);
- await framePromise();
- expect(button.node.getElementsByTagName('svg')[0].dataset.icon).toMatch(
- icon.name
- );
- });
- it('should render the customized icon command', async () => {
- const icon = jupyterIcon;
- const buttonIcon = blankIcon;
- commands.addCommand(id, { ...options, icon });
- const button = new CommandToolbarButton({
- commands,
- id,
- icon: buttonIcon
- });
- Widget.attach(button, document.body);
- await framePromise();
- const iconSVG = button.node.getElementsByTagName('svg')[0];
- expect(iconSVG.dataset.icon).toMatch(buttonIcon.name);
- expect(iconSVG.dataset.icon).not.toMatch(icon.name);
- });
- });
- describe('Toolbar', () => {
- let widget: Toolbar<Widget>;
- beforeEach(async () => {
- jest.setTimeout(20000);
- widget = new Toolbar();
- });
- afterEach(async () => {
- widget.dispose();
- });
- describe('#constructor()', () => {
- it('should construct a new toolbar widget', () => {
- const widget = new Toolbar();
- expect(widget).toBeInstanceOf(Toolbar);
- });
- it('should add the `jp-Toolbar` class', () => {
- const widget = new Toolbar();
- expect(widget.hasClass('jp-Toolbar')).toBe(true);
- });
- });
- describe('#names()', () => {
- it('should get an ordered list the toolbar item names', () => {
- widget.addItem('foo', new Widget());
- widget.addItem('bar', new Widget());
- widget.addItem('baz', new Widget());
- expect(toArray(widget.names())).toEqual(['foo', 'bar', 'baz']);
- });
- });
- describe('#addItem()', () => {
- it('should add an item to the toolbar', () => {
- const item = new Widget();
- expect(widget.addItem('test', item)).toBe(true);
- expect(toArray(widget.names())).toContain('test');
- });
- it('should add the `jp-Toolbar-item` class to the widget', () => {
- const item = new Widget();
- widget.addItem('test', item);
- expect(item.hasClass('jp-Toolbar-item')).toBe(true);
- });
- it('should return false if the name is already used', () => {
- widget.addItem('test', new Widget());
- expect(widget.addItem('test', new Widget())).toBe(false);
- });
- });
- describe('#insertItem()', () => {
- it('should insert the item into the toolbar', () => {
- widget.addItem('a', new Widget());
- widget.addItem('b', new Widget());
- widget.insertItem(1, 'c', new Widget());
- expect(toArray(widget.names())).toEqual(['a', 'c', 'b']);
- });
- it('should clamp the bounds', () => {
- widget.addItem('a', new Widget());
- widget.addItem('b', new Widget());
- widget.insertItem(10, 'c', new Widget());
- expect(toArray(widget.names())).toEqual(['a', 'b', 'c']);
- });
- });
- describe('#insertAfter()', () => {
- it('should insert an item into the toolbar after `c`', () => {
- widget.addItem('a', new Widget());
- widget.addItem('b', new Widget());
- widget.insertItem(1, 'c', new Widget());
- widget.insertAfter('c', 'd', new Widget());
- expect(toArray(widget.names())).toEqual(['a', 'c', 'd', 'b']);
- });
- it('should return false if the target item does not exist', () => {
- widget.addItem('a', new Widget());
- widget.addItem('b', new Widget());
- const value = widget.insertAfter('c', 'd', new Widget());
- expect(value).toBe(false);
- });
- });
- describe('#insertBefore()', () => {
- it('should insert an item into the toolbar before `c`', () => {
- widget.addItem('a', new Widget());
- widget.addItem('b', new Widget());
- widget.insertItem(1, 'c', new Widget());
- widget.insertBefore('c', 'd', new Widget());
- expect(toArray(widget.names())).toEqual(['a', 'd', 'c', 'b']);
- });
- it('should return false if the target item does not exist', () => {
- widget.addItem('a', new Widget());
- widget.addItem('b', new Widget());
- const value = widget.insertBefore('c', 'd', new Widget());
- expect(value).toBe(false);
- });
- });
- describe('.createFromCommand', () => {
- const commands = new CommandRegistry();
- const testLogCommandId = 'test:toolbar-log';
- const logArgs: ReadonlyPartialJSONObject[] = [];
- let enabled = false;
- let toggled = true;
- let visible = false;
- commands.addCommand(testLogCommandId, {
- execute: args => {
- logArgs.push(args);
- },
- label: 'Test log command label',
- caption: 'Test log command caption',
- usage: 'Test log command usage',
- iconClass: 'test-icon-class',
- className: 'test-log-class',
- isEnabled: () => enabled,
- isToggled: () => toggled,
- isVisible: () => visible
- });
- async function render(button: CommandToolbarButton) {
- button.update();
- await framePromise();
- expect(button.renderPromise).toBeDefined();
- await button.renderPromise;
- }
- it('should create a button', () => {
- const button = new CommandToolbarButton({
- commands,
- id: testLogCommandId
- });
- expect(button).toBeInstanceOf(CommandToolbarButton);
- button.dispose();
- });
- it('should add main class', async () => {
- const button = new CommandToolbarButton({
- commands,
- id: testLogCommandId
- });
- await render(button);
- const buttonNode = button.node.firstChild as HTMLButtonElement;
- expect(buttonNode.classList.contains('test-log-class')).toBe(true);
- button.dispose();
- });
- it('should add an icon with icon class and label', async () => {
- const button = new CommandToolbarButton({
- commands,
- id: testLogCommandId
- });
- await render(button);
- const buttonNode = button.node.firstChild as HTMLButtonElement;
- expect(buttonNode.title).toBe('Test log command caption');
- const wrapperNode = buttonNode.firstChild as HTMLElement;
- const iconNode = wrapperNode.firstChild as HTMLElement;
- expect(iconNode.classList.contains('test-icon-class')).toBe(true);
- button.dispose();
- });
- it('should apply state classes', async () => {
- enabled = false;
- toggled = true;
- visible = false;
- const button = new CommandToolbarButton({
- commands,
- id: testLogCommandId
- });
- await render(button);
- const buttonNode = button.node.firstChild as HTMLButtonElement;
- expect(buttonNode.disabled).toBe(true);
- expect(buttonNode.classList.contains('lm-mod-toggled')).toBe(true);
- expect(buttonNode.classList.contains('lm-mod-hidden')).toBe(true);
- button.dispose();
- });
- it('should update state classes', async () => {
- enabled = false;
- toggled = true;
- visible = false;
- const button = new CommandToolbarButton({
- commands,
- id: testLogCommandId
- });
- await render(button);
- const buttonNode = button.node.firstChild as HTMLButtonElement;
- expect(buttonNode.disabled).toBe(true);
- expect(buttonNode.classList.contains('lm-mod-toggled')).toBe(true);
- expect(buttonNode.classList.contains('lm-mod-hidden')).toBe(true);
- enabled = true;
- visible = true;
- commands.notifyCommandChanged(testLogCommandId);
- expect(buttonNode.disabled).toBe(false);
- expect(buttonNode.classList.contains('lm-mod-toggled')).toBe(true);
- expect(buttonNode.classList.contains('lm-mod-hidden')).toBe(false);
- enabled = false;
- visible = false;
- button.dispose();
- });
- it('should use the command label if no icon class/label', async () => {
- const id = 'to-be-removed';
- const cmd = commands.addCommand(id, {
- execute: () => {
- return;
- },
- label: 'Label-only button'
- });
- const button = new CommandToolbarButton({
- commands,
- id
- });
- await render(button);
- const buttonNode = button.node.firstChild as HTMLButtonElement;
- expect(buttonNode.textContent).toBe('Label-only button');
- cmd.dispose();
- });
- it('should update the node content on command change event', async () => {
- const id = 'to-be-removed';
- let iconClassValue: string = '';
- const cmd = commands.addCommand(id, {
- execute: () => {
- /* no op */
- },
- label: 'Label-only button',
- iconClass: () => iconClassValue ?? ''
- });
- const button = new CommandToolbarButton({
- commands,
- id
- });
- await render(button);
- const buttonNode = button.node.firstChild as HTMLButtonElement;
- expect(buttonNode.textContent).toBe('Label-only button');
- expect(buttonNode.classList.contains(iconClassValue)).toBe(false);
- iconClassValue = 'updated-icon-class';
- commands.notifyCommandChanged(id);
- await render(button);
- const wrapperNode = buttonNode.firstChild as HTMLElement;
- const iconNode = wrapperNode.firstChild as HTMLElement;
- expect(iconNode.classList.contains(iconClassValue)).toBe(true);
- cmd.dispose();
- });
- });
- describe('Kernel buttons', () => {
- let sessionContext: SessionContext;
- beforeEach(async () => {
- sessionContext = await createSessionContext();
- });
- afterEach(async () => {
- await sessionContext.shutdown();
- sessionContext.dispose();
- });
- describe('.createInterruptButton()', () => {
- it("should add an inline svg node with the 'stop' icon", async () => {
- const button = Toolbar.createInterruptButton(sessionContext);
- Widget.attach(button, document.body);
- await framePromise();
- expect(
- button.node.querySelector("[data-icon$='stop']")
- ).toBeDefined();
- });
- });
- describe('.createRestartButton()', () => {
- it("should add an inline svg node with the 'refresh' icon", async () => {
- const button = Toolbar.createRestartButton(sessionContext);
- Widget.attach(button, document.body);
- await framePromise();
- expect(
- button.node.querySelector("[data-icon$='refresh']")
- ).toBeDefined();
- });
- });
- describe('.createKernelNameItem()', () => {
- it("should display the `'display_name'` of the kernel", async () => {
- const item = Toolbar.createKernelNameItem(sessionContext);
- await sessionContext.initialize();
- Widget.attach(item, document.body);
- await framePromise();
- const node = item.node.querySelector(
- '.jp-ToolbarButtonComponent-label'
- )!;
- expect(node.textContent).toBe(sessionContext.kernelDisplayName);
- });
- });
- describe('.createKernelStatusItem()', () => {
- beforeEach(async () => {
- await sessionContext.initialize();
- await sessionContext.session?.kernel?.info;
- });
- it('should display a busy status if the kernel status is busy', async () => {
- const item = Toolbar.createKernelStatusItem(sessionContext);
- let called = false;
- sessionContext.statusChanged.connect((_, status) => {
- if (status === 'busy') {
- expect(
- item.node.querySelector("[data-icon$='circle']")
- ).toBeDefined();
- called = true;
- }
- });
- const future = sessionContext.session?.kernel?.requestExecute({
- code: 'a = 109\na'
- })!;
- await future.done;
- expect(called).toBe(true);
- });
- it('should show the current status in the node title', async () => {
- const item = Toolbar.createKernelStatusItem(sessionContext);
- const status = sessionContext.session?.kernel?.status;
- expect(item.node.title.toLowerCase()).toContain(status);
- let called = false;
- const future = sessionContext.session?.kernel?.requestExecute({
- code: 'a = 1'
- })!;
- future.onIOPub = msg => {
- if (sessionContext.session?.kernel?.status === 'busy') {
- expect(item.node.title.toLowerCase()).toContain('busy');
- called = true;
- }
- };
- await future.done;
- expect(called).toBe(true);
- });
- it('should handle a starting session', async () => {
- await sessionContext.session?.kernel?.info;
- await sessionContext.shutdown();
- sessionContext = await createSessionContext();
- await sessionContext.initialize();
- const item = Toolbar.createKernelStatusItem(sessionContext);
- expect(item.node.title).toBe('Kernel Connecting');
- expect(
- item.node.querySelector("[data-icon$='circle-empty']")
- ).toBeDefined();
- await sessionContext.initialize();
- await sessionContext.session?.kernel?.info;
- });
- });
- });
- });
- describe('ReactiveToolbar', () => {
- let toolbar: ReactiveToolbar;
- beforeEach(() => {
- toolbar = new ReactiveToolbar();
- Widget.attach(toolbar, document.body);
- });
- afterEach(() => {
- toolbar.dispose();
- });
- describe('#constructor()', () => {
- it('should append a node to body for the pop-up', () => {
- const popup = document.body.querySelector(
- '.jp-Toolbar-responsive-popup'
- );
- expect(popup).toBeDefined();
- expect(popup!.parentNode!.nodeName).toEqual('BODY');
- });
- });
- describe('#addItem()', () => {
- it('should insert item before the toolbar pop-up button', () => {
- const w = new Widget();
- toolbar.addItem('test', w);
- expect(
- (toolbar.layout as PanelLayout).widgets.findIndex(v => v === w)
- ).toEqual((toolbar.layout as PanelLayout).widgets.length - 2);
- });
- });
- describe('#insertItem()', () => {
- it('should insert item before the toolbar pop-up button', () => {
- const w = new Widget();
- toolbar.insertItem(2, 'test', w);
- expect(
- (toolbar.layout as PanelLayout).widgets.findIndex(v => v === w)
- ).toEqual((toolbar.layout as PanelLayout).widgets.length - 2);
- });
- });
- describe('#insertAfter()', () => {
- it('should not insert item after the toolbar pop-up button', () => {
- const w = new Widget();
- const r = toolbar.insertAfter('toolbar-popup-opener', 'test', w);
- expect(r).toEqual(false);
- expect(
- (toolbar.layout as PanelLayout).widgets.findIndex(v => v === w)
- ).toEqual(-1);
- });
- });
- });
- describe('ToolbarButton', () => {
- describe('#constructor()', () => {
- it('should accept no arguments', () => {
- const widget = new ToolbarButton();
- expect(widget).toBeInstanceOf(ToolbarButton);
- });
- it('should accept options', async () => {
- const widget = new ToolbarButton({
- className: 'foo',
- iconClass: 'iconFoo',
- onClick: () => {
- return void 0;
- },
- tooltip: 'bar'
- });
- Widget.attach(widget, document.body);
- await framePromise();
- const button = widget.node.firstChild as HTMLElement;
- expect(button.classList.contains('foo')).toBe(true);
- expect(button.querySelector('.iconFoo')).toBeDefined();
- expect(button.title).toBe('bar');
- });
- });
- describe('#dispose()', () => {
- it('should dispose of the resources used by the widget', () => {
- const button = new ToolbarButton();
- button.dispose();
- expect(button.isDisposed).toBe(true);
- });
- it('should be safe to call more than once', () => {
- const button = new ToolbarButton();
- button.dispose();
- button.dispose();
- expect(button.isDisposed).toBe(true);
- });
- });
- describe('#handleEvent()', () => {
- describe('click', () => {
- it('should activate the callback', async () => {
- let called = false;
- const button = new ToolbarButton({
- onClick: () => {
- called = true;
- }
- });
- Widget.attach(button, document.body);
- await framePromise();
- simulate(button.node.firstChild as HTMLElement, 'mousedown');
- expect(called).toBe(true);
- button.dispose();
- });
- });
- describe('keydown', () => {
- it('Enter should activate the callback', async () => {
- let called = false;
- const button = new ToolbarButton({
- onClick: () => {
- called = true;
- }
- });
- Widget.attach(button, document.body);
- await framePromise();
- simulate(button.node.firstChild as HTMLElement, 'keydown', {
- key: 'Enter'
- });
- expect(called).toBe(true);
- button.dispose();
- });
- it('Space should activate the callback', async () => {
- let called = false;
- const button = new ToolbarButton({
- onClick: () => {
- called = true;
- }
- });
- Widget.attach(button, document.body);
- await framePromise();
- simulate(button.node.firstChild as HTMLElement, 'keydown', {
- key: ' '
- });
- expect(called).toBe(true);
- button.dispose();
- });
- });
- });
- describe('#pressed()', () => {
- it('should update the pressed state', async () => {
- const widget = new ToolbarButton({
- icon: bugIcon,
- tooltip: 'tooltip',
- pressedTooltip: 'pressed tooltip',
- pressedIcon: bugDotIcon
- });
- Widget.attach(widget, document.body);
- await framePromise();
- const button = widget.node.firstChild as HTMLElement;
- expect(widget.pressed).toBe(false);
- expect(button.title).toBe('tooltip');
- expect(button.getAttribute('aria-pressed')).toEqual('false');
- let icon = button.querySelectorAll('svg');
- expect(icon[0].getAttribute('data-icon')).toEqual('ui-components:bug');
- widget.pressed = true;
- await framePromise();
- expect(widget.pressed).toBe(true);
- expect(button.title).toBe('pressed tooltip');
- expect(button.getAttribute('aria-pressed')).toEqual('true');
- icon = button.querySelectorAll('svg');
- expect(icon[0].getAttribute('data-icon')).toEqual(
- 'ui-components:bug-dot'
- );
- widget.dispose();
- });
- it('should not have the pressed state when not enabled', async () => {
- const widget = new ToolbarButton({
- icon: bugIcon,
- tooltip: 'tooltip',
- pressedTooltip: 'pressed tooltip',
- disabledTooltip: 'disabled tooltip',
- pressedIcon: bugDotIcon,
- enabled: false
- });
- Widget.attach(widget, document.body);
- await framePromise();
- const button = widget.node.firstChild as HTMLElement;
- expect(widget.pressed).toBe(false);
- expect(button.title).toBe('disabled tooltip');
- expect(button.getAttribute('aria-pressed')).toEqual('false');
- widget.pressed = true;
- await framePromise();
- expect(widget.pressed).toBe(false);
- expect(button.title).toBe('disabled tooltip');
- expect(button.getAttribute('aria-pressed')).toEqual('false');
- const icon = button.querySelectorAll('svg');
- expect(icon[0].getAttribute('data-icon')).toEqual('ui-components:bug');
- widget.dispose();
- });
- });
- describe('#enabled()', () => {
- it('should update the enabled state', async () => {
- const widget = new ToolbarButton({
- icon: bugIcon,
- tooltip: 'tooltip',
- pressedTooltip: 'pressed tooltip',
- disabledTooltip: 'disabled tooltip',
- pressedIcon: bugDotIcon
- });
- Widget.attach(widget, document.body);
- await framePromise();
- const button = widget.node.firstChild as HTMLElement;
- expect(widget.enabled).toBe(true);
- expect(widget.pressed).toBe(false);
- expect(button.getAttribute('aria-disabled')).toEqual('false');
- widget.pressed = true;
- await framePromise();
- expect(widget.pressed).toBe(true);
- widget.enabled = false;
- await framePromise();
- expect(widget.enabled).toBe(false);
- expect(widget.pressed).toBe(false);
- expect(button.getAttribute('aria-disabled')).toEqual('true');
- widget.dispose();
- });
- });
- describe('#onClick()', () => {
- it('should update the onClick state', async () => {
- let mockCalled = false;
- const mockOnClick = () => {
- mockCalled = true;
- };
- const widget = new ToolbarButton({
- icon: bugIcon,
- tooltip: 'tooltip',
- onClick: mockOnClick
- });
- Widget.attach(widget, document.body);
- await framePromise();
- simulate(widget.node.firstChild as HTMLElement, 'mousedown');
- expect(mockCalled).toBe(true);
- mockCalled = false;
- let mockUpdatedCalled = false;
- const mockOnClickUpdated = () => {
- mockUpdatedCalled = true;
- };
- widget.onClick = mockOnClickUpdated;
- await framePromise();
- simulate(widget.node.firstChild as HTMLElement, 'mousedown');
- expect(mockCalled).toBe(false);
- expect(mockUpdatedCalled).toBe(true);
- widget.dispose();
- });
- });
- });
- describe('ToolbarWidgetRegistry', () => {
- describe('#constructor', () => {
- it('should set a default factory', () => {
- const dummy = jest.fn();
- const registry = new ToolbarWidgetRegistry({
- defaultFactory: dummy
- });
- expect(registry.defaultFactory).toBe(dummy);
- });
- });
- describe('#defaultFactory', () => {
- it('should set a default factory', () => {
- const dummy = jest.fn();
- const dummy2 = jest.fn();
- const registry = new ToolbarWidgetRegistry({
- defaultFactory: dummy
- });
- registry.defaultFactory = dummy2;
- expect(registry.defaultFactory).toBe(dummy2);
- });
- });
- describe('#createWidget', () => {
- it('should call the default factory as fallback', () => {
- const documentWidget = new Widget();
- const dummyWidget = new Widget();
- const dummy = jest.fn().mockReturnValue(dummyWidget);
- const registry = new ToolbarWidgetRegistry({
- defaultFactory: dummy
- });
- const item: ToolbarRegistry.IWidget = {
- name: 'test'
- };
- const widget = registry.createWidget('factory', documentWidget, item);
- expect(widget).toBe(dummyWidget);
- expect(dummy).toBeCalledWith('factory', documentWidget, item);
- });
- it('should call the registered factory', () => {
- const documentWidget = new Widget();
- const dummyWidget = new Widget();
- const defaultFactory = jest.fn().mockReturnValue(dummyWidget);
- const dummy = jest.fn().mockReturnValue(dummyWidget);
- const registry = new ToolbarWidgetRegistry({
- defaultFactory
- });
- const item: ToolbarRegistry.IWidget = {
- name: 'test'
- };
- registry.registerFactory('factory', item.name, dummy);
- const widget = registry.createWidget('factory', documentWidget, item);
- expect(widget).toBe(dummyWidget);
- expect(dummy).toBeCalledWith(documentWidget);
- expect(defaultFactory).toBeCalledTimes(0);
- });
- });
- describe('#registerFactory', () => {
- it('should return the previous registered factory', () => {
- const defaultFactory = jest.fn();
- const dummy = jest.fn();
- const dummy2 = jest.fn();
- const registry = new ToolbarWidgetRegistry({
- defaultFactory
- });
- const item: ToolbarRegistry.IWidget = {
- name: 'test'
- };
- expect(
- registry.registerFactory('factory', item.name, dummy)
- ).toBeUndefined();
- expect(registry.registerFactory('factory', item.name, dummy2)).toBe(
- dummy
- );
- });
- });
- });
- describe('createToolbarFactory', () => {
- it('should return the toolbar items', async () => {
- const factoryName = 'dummyFactory';
- const pluginId = 'test-plugin:settings';
- const toolbarRegistry = new ToolbarWidgetRegistry({
- defaultFactory: jest.fn()
- });
- const bar: ISettingRegistry.IPlugin = {
- data: {
- composite: {},
- user: {}
- },
- id: pluginId,
- raw: '{}',
- schema: {
- 'jupyter.lab.toolbars': {
- dummyFactory: [
- {
- name: 'insert',
- command: 'notebook:insert-cell-below',
- rank: 20
- },
- { name: 'spacer', type: 'spacer', rank: 100 },
- { name: 'cut', command: 'notebook:cut-cell', rank: 21 },
- {
- name: 'clear-all',
- command: 'notebook:clear-all-cell-outputs',
- rank: 60,
- disabled: true
- }
- ]
- },
- 'jupyter.lab.transform': true,
- properties: {
- toolbar: {
- type: 'array'
- }
- },
- type: 'object'
- },
- version: 'test'
- };
- const connector: IDataConnector<
- ISettingRegistry.IPlugin,
- string,
- string,
- string
- > = {
- fetch: jest.fn().mockImplementation((id: string) => {
- switch (id) {
- case bar.id:
- return bar;
- default:
- return {};
- }
- }),
- list: jest.fn(),
- save: jest.fn(),
- remove: jest.fn()
- };
- const settingRegistry = new SettingRegistry({
- connector
- });
- const translator: ITranslator = {
- load: jest.fn()
- };
- const factory = createToolbarFactory(
- toolbarRegistry,
- settingRegistry,
- factoryName,
- pluginId,
- translator
- );
- await settingRegistry.load(bar.id);
- const items = factory(new Widget());
- expect(items).toHaveLength(3);
- expect(items.get(0).name).toEqual('insert');
- expect(items.get(1).name).toEqual('cut');
- expect(items.get(2).name).toEqual('spacer');
- });
- it('should update the toolbar items with late settings load', async () => {
- const factoryName = 'dummyFactory';
- const pluginId = 'test-plugin:settings';
- const toolbarRegistry = new ToolbarWidgetRegistry({
- defaultFactory: jest.fn()
- });
- const foo: ISettingRegistry.IPlugin = {
- data: {
- composite: {},
- user: {}
- },
- id: 'foo',
- raw: '{}',
- schema: {
- 'jupyter.lab.toolbars': {
- dummyFactory: [
- { name: 'cut', command: 'notebook:cut-cell', rank: 21 },
- { name: 'insert', rank: 40 },
- {
- name: 'clear-all',
- disabled: true
- }
- ]
- },
- type: 'object'
- },
- version: 'test'
- };
- const bar: ISettingRegistry.IPlugin = {
- data: {
- composite: {},
- user: {}
- },
- id: pluginId,
- raw: '{}',
- schema: {
- 'jupyter.lab.toolbars': {
- dummyFactory: [
- {
- name: 'insert',
- command: 'notebook:insert-cell-below',
- rank: 20
- },
- {
- name: 'clear-all',
- command: 'notebook:clear-all-cell-outputs',
- rank: 60
- }
- ]
- },
- 'jupyter.lab.transform': true,
- properties: {
- toolbar: {
- type: 'array'
- }
- },
- type: 'object'
- },
- version: 'test'
- };
- const connector: IDataConnector<
- ISettingRegistry.IPlugin,
- string,
- string,
- string
- > = {
- fetch: jest.fn().mockImplementation((id: string) => {
- switch (id) {
- case bar.id:
- return bar;
- case foo.id:
- return foo;
- default:
- return {};
- }
- }),
- list: jest.fn(),
- save: jest.fn(),
- remove: jest.fn()
- };
- const settingRegistry = new SettingRegistry({
- connector
- });
- const translator: ITranslator = {
- load: jest.fn()
- };
- const factory = createToolbarFactory(
- toolbarRegistry,
- settingRegistry,
- factoryName,
- pluginId,
- translator
- );
- const barPlugin = await settingRegistry.load(bar.id);
- const baseToolbar = JSONExt.deepCopy(
- barPlugin.composite['toolbar'] as any
- );
- let waitForChange = new PromiseDelegate<void>();
- barPlugin.changed.connect(() => {
- if (
- !JSONExt.deepEqual(baseToolbar, barPlugin.composite['toolbar'] as any)
- ) {
- waitForChange.resolve();
- }
- });
- await settingRegistry.load(foo.id);
- await waitForChange.promise;
- const items = factory(new Widget());
- expect(items).toHaveLength(2);
- expect(items.get(0).name).toEqual('cut');
- expect(items.get(1).name).toEqual('insert');
- });
- it('should be callable multiple times', async () => {
- const factoryName = 'dummyFactory';
- const pluginId = 'test-plugin:settings';
- const toolbarRegistry = new ToolbarWidgetRegistry({
- defaultFactory: jest.fn()
- });
- const bar: ISettingRegistry.IPlugin = {
- data: {
- composite: {},
- user: {}
- },
- id: pluginId,
- raw: '{}',
- schema: {
- 'jupyter.lab.toolbars': {
- dummyFactory: [
- {
- name: 'insert',
- command: 'notebook:insert-cell-below',
- rank: 20
- },
- { name: 'spacer', type: 'spacer', rank: 100 },
- { name: 'cut', command: 'notebook:cut-cell', rank: 21 },
- {
- name: 'clear-all',
- command: 'notebook:clear-all-cell-outputs',
- rank: 60,
- disabled: true
- }
- ]
- },
- 'jupyter.lab.transform': true,
- properties: {
- toolbar: {
- type: 'array'
- }
- },
- type: 'object'
- },
- version: 'test'
- };
- const connector: IDataConnector<
- ISettingRegistry.IPlugin,
- string,
- string,
- string
- > = {
- fetch: jest.fn().mockImplementation((id: string) => {
- switch (id) {
- case bar.id:
- return bar;
- default:
- return {};
- }
- }),
- list: jest.fn(),
- save: jest.fn(),
- remove: jest.fn()
- };
- const settingRegistry = new SettingRegistry({
- connector
- });
- const translator: ITranslator = {
- load: jest.fn()
- };
- const factory = createToolbarFactory(
- toolbarRegistry,
- settingRegistry,
- factoryName,
- pluginId,
- translator
- );
- const factory2 = createToolbarFactory(
- toolbarRegistry,
- settingRegistry,
- factoryName,
- pluginId,
- translator
- );
- await settingRegistry.load(bar.id);
- expect(factory(new Widget())).toHaveLength(3);
- expect(factory2(new Widget())).toHaveLength(3);
- });
- });
- });
|