12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046 |
- // 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 { 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);
- // Trick push this test after all other promise in the hope they get resolve
- // before going further - in particular we are looking at the update of the items
- // factory in `createToolbarFactory`
- await Promise.resolve();
- 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
- );
- await settingRegistry.load(bar.id);
- // Trick push this test after all other promise in the hope they get resolve
- // before going further - in particular we are looking at the update of the items
- // factory in `createToolbarFactory`
- await Promise.resolve();
- await settingRegistry.load(foo.id);
- const items = factory(new Widget());
- expect(items).toHaveLength(2);
- expect(items.get(0).name).toEqual('cut');
- expect(items.get(1).name).toEqual('insert');
- });
- });
- });
|