123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import 'jest';
- import { ServiceManager } from '@jupyterlab/services';
- import { Widget } from '@lumino/widgets';
- import { DocumentManager } from '../src';
- import {
- DocumentRegistry,
- TextModelFactory,
- ABCWidgetFactory,
- DocumentWidget,
- IDocumentWidget
- } from '@jupyterlab/docregistry';
- import { dismissDialog } from '@jupyterlab/testutils';
- import * as Mock from '@jupyterlab/testutils/lib/mock';
- class WidgetFactory extends ABCWidgetFactory<IDocumentWidget> {
- protected createNewWidget(
- context: DocumentRegistry.Context
- ): IDocumentWidget {
- const content = new Widget();
- const widget = new DocumentWidget({ content, context });
- widget.addClass('WidgetFactory');
- return widget;
- }
- }
- /**
- * A test documentWidget that maintains some state in
- * count
- */
- class CloneTestWidget extends DocumentWidget {
- constructor(args: any) {
- super(args);
- this.counter = args.count;
- }
- counter: number = 0;
- }
- /**
- * A widget factory for CloneTestWidget widgets
- */
- class WidgetFactoryWithSharedState extends ABCWidgetFactory<CloneTestWidget> {
- protected createNewWidget(
- context: DocumentRegistry.Context,
- source: CloneTestWidget
- ): CloneTestWidget {
- return new CloneTestWidget({
- context,
- content: new Widget(),
- count: source ? source.counter + 1 : 0
- });
- }
- }
- describe('@jupyterlab/docmanager', () => {
- let manager: DocumentManager;
- let services: ServiceManager.IManager;
- let context: DocumentRegistry.Context;
- let widget: Widget | undefined;
- const textModelFactory = new TextModelFactory();
- const widgetFactory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- canStartKernel: true,
- preferKernel: true
- });
- const widgetFactoryShared = new WidgetFactoryWithSharedState({
- name: 'CloneTestWidget',
- fileTypes: []
- });
- beforeAll(() => {
- services = new Mock.ServiceManagerMock();
- });
- beforeEach(() => {
- const registry = new DocumentRegistry({ textModelFactory });
- registry.addWidgetFactory(widgetFactory);
- registry.addWidgetFactory(widgetFactoryShared);
- DocumentRegistry.defaultFileTypes.forEach(ft => {
- registry.addFileType(ft);
- });
- manager = new DocumentManager({
- registry,
- manager: services,
- opener: {
- open: (widget: Widget) => {
- // no-op
- }
- }
- });
- });
- afterEach(() => {
- manager.dispose();
- });
- describe('DocumentWidgetManager', () => {
- describe('#constructor()', () => {
- it('should create a new document manager', () => {
- expect(manager).toBeInstanceOf(DocumentManager);
- });
- });
- describe('#isDisposed', () => {
- it('should test whether the manager is disposed', () => {
- expect(manager.isDisposed).toBe(false);
- manager.dispose();
- expect(manager.isDisposed).toBe(true);
- });
- });
- describe('#dispose()', () => {
- it('should dispose of the resources used by the manager', () => {
- expect(manager.isDisposed).toBe(false);
- manager.dispose();
- expect(manager.isDisposed).toBe(true);
- manager.dispose();
- expect(manager.isDisposed).toBe(true);
- });
- });
- describe('#services', () => {
- it('should get the service manager for the manager', async () => {
- await manager.services.ready;
- });
- });
- describe('#registry', () => {
- it('should get the registry used by the manager', () => {
- expect(manager.registry).toBeInstanceOf(DocumentRegistry);
- });
- });
- describe('#open()', () => {
- it('should open a file and return the widget used to view it', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.open(model.path)!;
- expect(widget.hasClass('WidgetFactory')).toBe(true);
- await dismissDialog();
- });
- it('should start a kernel if one is given', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- const session = await services.sessions.startNew({
- name: '',
- path: model.path,
- type: 'test'
- });
- const id = session.kernel!.id;
- widget = manager.open(session.path, 'default', { id })!;
- context = manager.contextForWidget(widget)!;
- await context.ready;
- await context.sessionContext.ready;
- expect(context.sessionContext.session?.kernel).toBeTruthy();
- await context.sessionContext.shutdown();
- });
- it('should not auto-start a kernel if there is none given', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.open(model.path, 'default')!;
- context = manager.contextForWidget(widget)!;
- await dismissDialog();
- expect(context.sessionContext.session?.kernel).toBeFalsy();
- });
- it('should return undefined if the factory is not found', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.open(model.path, 'foo');
- expect(widget).toBeUndefined();
- });
- it('should return undefined if the factory has no model factory', async () => {
- const widgetFactory2 = new WidgetFactory({
- name: 'test',
- modelName: 'foo',
- fileTypes: ['text']
- });
- manager.registry.addWidgetFactory(widgetFactory2);
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.open(model.path, 'foo');
- expect(widget).toBeUndefined();
- });
- });
- describe('#createNew()', () => {
- it('should open a file and return the widget used to view it', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path)!;
- expect(widget.hasClass('WidgetFactory')).toBe(true);
- await dismissDialog();
- });
- it('should start a kernel if one is given', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- const session = await services.sessions.startNew({
- name: '',
- path: model.path,
- type: 'test'
- });
- const id = session.kernel!.id;
- widget = manager.createNew(session.path, 'default', { id })!;
- context = manager.contextForWidget(widget)!;
- await context.ready;
- await context.sessionContext.ready;
- expect(context.sessionContext.session!.kernel!.id).toBe(id);
- await context.sessionContext.shutdown();
- });
- it('should not start a kernel if not given', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path, 'default')!;
- context = manager.contextForWidget(widget)!;
- await dismissDialog();
- expect(context.sessionContext.session?.kernel).toBeFalsy();
- });
- it('should return undefined if the factory is not found', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path, 'foo');
- expect(widget).toBeUndefined();
- });
- it('should return undefined if the factory has no model factory', async () => {
- const widgetFactory2 = new WidgetFactory({
- name: 'test',
- modelName: 'foo',
- fileTypes: ['text']
- });
- manager.registry.addWidgetFactory(widgetFactory2);
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path, 'foo');
- expect(widget).toBeUndefined();
- });
- });
- describe('#findWidget()', () => {
- it('should find a widget given a file and a widget name', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path);
- expect(manager.findWidget(model.path, 'test')).toBe(widget);
- await dismissDialog();
- });
- it('should find a widget given a file', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path);
- expect(manager.findWidget(model.path)).toBe(widget);
- await dismissDialog();
- });
- it('should fail to find a widget', () => {
- expect(manager.findWidget('foo')).toBeUndefined();
- });
- it('should fail to find a widget with non default factory and the default widget name', async () => {
- const widgetFactory2 = new WidgetFactory({
- name: 'test2',
- fileTypes: ['text']
- });
- manager.registry.addWidgetFactory(widgetFactory2);
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path, 'test2');
- expect(manager.findWidget(model.path)).toBeUndefined();
- });
- it('should find a widget with non default factory given a file and a null widget name', async () => {
- const widgetFactory2 = new WidgetFactory({
- name: 'test2',
- fileTypes: ['text']
- });
- manager.registry.addWidgetFactory(widgetFactory2);
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path, 'test2');
- expect(manager.findWidget(model.path, null)).toBe(widget);
- await dismissDialog();
- });
- });
- describe('#contextForWidget()', () => {
- it('should find the context for a widget', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path)!;
- context = manager.contextForWidget(widget)!;
- expect(context.path).toBe(model.path);
- await dismissDialog();
- });
- it('should fail to find the context for the widget', () => {
- widget = new Widget();
- expect(manager.contextForWidget(widget)).toBeUndefined();
- });
- });
- describe('#cloneWidget()', () => {
- it('should clone the given widget', async () => {
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- widget = manager.createNew(model.path)!;
- const clone = manager.cloneWidget(widget)!;
- expect(manager.contextForWidget(widget)).toBe(
- manager.contextForWidget(clone)
- );
- await dismissDialog();
- });
- it('should return undefined if the source widget is not managed', () => {
- widget = new Widget();
- expect(manager.cloneWidget(widget)).toBeUndefined();
- });
- it('should allow widget factories to have custom clone behavior', () => {
- widget = manager.createNew('foo', 'CloneTestWidget')!;
- const clonedWidget: CloneTestWidget = manager.cloneWidget(
- widget
- ) as CloneTestWidget;
- expect(clonedWidget.counter).toBe(1);
- const newWidget: CloneTestWidget = manager.createNew(
- 'bar',
- 'CloneTestWidget'
- ) as CloneTestWidget;
- expect(newWidget.counter).toBe(0);
- expect(
- (manager.cloneWidget(clonedWidget) as CloneTestWidget).counter
- ).toBe(2);
- });
- });
- describe('#closeFile()', () => {
- it('should close the widgets associated with a given path', async () => {
- let called = 0;
- let path = '';
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- path = model.path;
- widget = manager.createNew(path)!;
- const clone = manager.cloneWidget(widget)!;
- widget.disposed.connect(() => {
- called++;
- });
- clone.disposed.connect(() => {
- called++;
- });
- await dismissDialog();
- await manager.closeFile(path);
- expect(called).toBe(2);
- });
- it('should be a no-op if there are no open files on that path', () => {
- return manager.closeFile('foo');
- });
- });
- describe('#closeAll()', () => {
- it('should close all of the open documents', async () => {
- let called = 0;
- let path = '';
- const model = await services.contents.newUntitled({
- type: 'file',
- ext: '.txt'
- });
- path = model.path;
- const widget0 = manager.createNew(path)!;
- widget0.disposed.connect(() => {
- called++;
- });
- await dismissDialog();
- const widget1 = manager.createNew(path)!;
- widget1.disposed.connect(() => {
- called++;
- });
- await dismissDialog();
- await manager.closeAll();
- expect(called).toBe(2);
- });
- it('should be a no-op if there are no open documents', async () => {
- await manager.closeAll();
- });
- });
- });
- });
|