123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import 'jest';
- import { toArray } from '@lumino/algorithm';
- import { UUID } from '@lumino/coreutils';
- import { Widget } from '@lumino/widgets';
- import {
- ABCWidgetFactory,
- Base64ModelFactory,
- DocumentModel,
- DocumentRegistry,
- DocumentWidget,
- IDocumentWidget,
- TextModelFactory,
- Context
- } from '@jupyterlab/docregistry';
- import { ServiceManager } from '@jupyterlab/services';
- import { sleep } 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;
- }
- }
- function createFactory(): WidgetFactory {
- return new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- }
- describe('docregistry/default', () => {
- describe('ABCWidgetFactory', () => {
- describe('#fileTypes', () => {
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.fileTypes).toEqual(['text']);
- });
- });
- describe('#name', () => {
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.name).toBe('test');
- });
- });
- describe('#defaultFor', () => {
- it('should default to an empty array', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.defaultFor).toEqual([]);
- });
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- defaultFor: ['text']
- });
- expect(factory.defaultFor).toEqual(['text']);
- });
- });
- describe('#defaultRendered', () => {
- it('should default to an empty array', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.defaultRendered).toEqual([]);
- });
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- defaultRendered: ['text']
- });
- expect(factory.defaultRendered).toEqual(['text']);
- });
- });
- describe('#readOnly', () => {
- it('should default to false', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.readOnly).toBe(false);
- });
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- readOnly: true
- });
- expect(factory.readOnly).toBe(true);
- });
- });
- describe('#modelName', () => {
- it('should default to `text`', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.modelName).toBe('text');
- });
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- modelName: 'notebook'
- });
- expect(factory.modelName).toBe('notebook');
- });
- });
- describe('#preferKernel', () => {
- it('should default to false', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.preferKernel).toBe(false);
- });
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- preferKernel: true
- });
- expect(factory.preferKernel).toBe(true);
- });
- });
- describe('#canStartKernel', () => {
- it('should default to false', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text']
- });
- expect(factory.canStartKernel).toBe(false);
- });
- it('should be the value passed in', () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- canStartKernel: true
- });
- expect(factory.canStartKernel).toBe(true);
- });
- it('should have toolbar items', async () => {
- const factory = new WidgetFactory({
- name: 'test',
- fileTypes: ['text'],
- toolbarFactory: () => [
- {
- name: 'foo',
- widget: new Widget()
- },
- {
- name: 'bar',
- widget: new Widget()
- }
- ]
- });
- const context = await Mock.createFileContext();
- const widget = factory.createNew(context);
- const widget2 = factory.createNew(context);
- expect(toArray(widget.toolbar.names())).toEqual(['foo', 'bar']);
- expect(toArray(widget2.toolbar.names())).toEqual(['foo', 'bar']);
- expect(toArray(widget.toolbar.children()).length).toBe(2);
- expect(toArray(widget2.toolbar.children()).length).toBe(2);
- });
- });
- describe('#isDisposed', () => {
- it('should get whether the factory has been disposed', () => {
- const factory = createFactory();
- expect(factory.isDisposed).toBe(false);
- factory.dispose();
- expect(factory.isDisposed).toBe(true);
- });
- });
- describe('#dispose()', () => {
- it('should dispose of the resources held by the factory', () => {
- const factory = createFactory();
- factory.dispose();
- expect(factory.isDisposed).toBe(true);
- });
- it('should be safe to call multiple times', () => {
- const factory = createFactory();
- factory.dispose();
- factory.dispose();
- expect(factory.isDisposed).toBe(true);
- });
- });
- describe('#createNew()', () => {
- it('should create a new widget given a document model and a context', async () => {
- const factory = createFactory();
- const context = await Mock.createFileContext();
- const widget = factory.createNew(context);
- expect(widget).toBeInstanceOf(Widget);
- });
- it('should take an optional source widget for cloning', async () => {
- const factory = createFactory();
- const context = await Mock.createFileContext();
- const widget = factory.createNew(context);
- const clonedWidget: IDocumentWidget = factory.createNew(
- context,
- widget
- );
- expect(clonedWidget).not.toBe(widget);
- expect(clonedWidget.hasClass('WidgetFactory')).toBe(true);
- expect(clonedWidget.context).toBe(widget.context);
- });
- });
- });
- describe('Base64ModelFactory', () => {
- describe('#name', () => {
- it('should get the name of the model type', () => {
- const factory = new Base64ModelFactory();
- expect(factory.name).toBe('base64');
- });
- });
- describe('#contentType', () => {
- it('should get the file type', () => {
- const factory = new Base64ModelFactory();
- expect(factory.contentType).toBe('file');
- });
- });
- describe('#fileFormat', () => {
- it('should get the file format', () => {
- const factory = new Base64ModelFactory();
- expect(factory.fileFormat).toBe('base64');
- });
- });
- });
- describe('DocumentModel', () => {
- describe('#constructor()', () => {
- it('should create a new document model', () => {
- const model = new DocumentModel();
- expect(model).toBeInstanceOf(DocumentModel);
- });
- it('should accept an optional language preference', () => {
- const model = new DocumentModel('foo');
- expect(model.defaultKernelLanguage).toBe('foo');
- });
- });
- describe('#isDisposed', () => {
- it('should get whether the model has been disposed', () => {
- const model = new DocumentModel();
- expect(model.isDisposed).toBe(false);
- model.dispose();
- expect(model.isDisposed).toBe(true);
- });
- });
- describe('#contentChanged', () => {
- it('should be emitted when the content of the model changes', () => {
- const model = new DocumentModel();
- let called = false;
- model.contentChanged.connect((sender, args) => {
- expect(sender).toBe(model);
- expect(args).toBeUndefined();
- called = true;
- });
- model.fromString('foo');
- expect(called).toBe(true);
- });
- it('should not be emitted if the content does not change', () => {
- const model = new DocumentModel();
- let called = false;
- model.contentChanged.connect(() => {
- called = true;
- });
- model.fromString('');
- expect(called).toBe(false);
- });
- });
- describe('#stateChanged', () => {
- it('should be emitted when the state of the model changes', () => {
- const model = new DocumentModel();
- let called = false;
- model.stateChanged.connect((sender, args) => {
- expect(sender).toBe(model);
- expect(args.name).toBe('readOnly');
- expect(args.oldValue).toBe(false);
- expect(args.newValue).toBe(true);
- called = true;
- });
- model.readOnly = true;
- expect(called).toBe(true);
- });
- it('should not be emitted if the state does not change', () => {
- const model = new DocumentModel();
- let called = false;
- model.stateChanged.connect(() => {
- called = true;
- });
- model.dirty = false;
- expect(called).toBe(false);
- });
- });
- describe('#dirty', () => {
- it('should get the dirty state of the document', () => {
- const model = new DocumentModel();
- expect(model.dirty).toBe(false);
- });
- it('should emit `stateChanged` when changed', () => {
- const model = new DocumentModel();
- let called = false;
- model.stateChanged.connect((sender, args) => {
- expect(sender).toBe(model);
- expect(args.name).toBe('dirty');
- expect(args.oldValue).toBe(false);
- expect(args.newValue).toBe(true);
- called = true;
- });
- model.dirty = true;
- expect(called).toBe(true);
- });
- it('should not emit `stateChanged` when not changed', () => {
- const model = new DocumentModel();
- let called = false;
- model.stateChanged.connect(() => {
- called = true;
- });
- model.dirty = false;
- expect(called).toBe(false);
- });
- });
- describe('#readOnly', () => {
- it('should get the read only state of the document', () => {
- const model = new DocumentModel();
- expect(model.readOnly).toBe(false);
- });
- it('should emit `stateChanged` when changed', () => {
- const model = new DocumentModel();
- let called = false;
- model.stateChanged.connect((sender, args) => {
- expect(sender).toBe(model);
- expect(args.name).toBe('readOnly');
- expect(args.oldValue).toBe(false);
- expect(args.newValue).toBe(true);
- called = true;
- });
- model.readOnly = true;
- expect(called).toBe(true);
- });
- it('should not emit `stateChanged` when not changed', () => {
- const model = new DocumentModel();
- let called = false;
- model.stateChanged.connect(() => {
- called = true;
- });
- model.readOnly = false;
- expect(called).toBe(false);
- });
- });
- describe('#defaultKernelName', () => {
- it('should get the default kernel name of the document', () => {
- const model = new DocumentModel();
- expect(model.defaultKernelName).toBe('');
- });
- });
- describe('defaultKernelLanguage', () => {
- it('should get the default kernel language of the document', () => {
- const model = new DocumentModel();
- expect(model.defaultKernelLanguage).toBe('');
- });
- it('should be set by the constructor arg', () => {
- const model = new DocumentModel('foo');
- expect(model.defaultKernelLanguage).toBe('foo');
- });
- });
- describe('#dispose()', () => {
- it('should dispose of the resources held by the document manager', () => {
- const model = new DocumentModel();
- model.dispose();
- expect(model.isDisposed).toBe(true);
- });
- it('should be safe to call more than once', () => {
- const model = new DocumentModel();
- model.dispose();
- model.dispose();
- expect(model.isDisposed).toBe(true);
- });
- });
- describe('#toString()', () => {
- it('should serialize the model to a string', () => {
- const model = new DocumentModel();
- expect(model.toString()).toBe('');
- });
- });
- describe('#fromString()', () => {
- it('should deserialize the model from a string', () => {
- const model = new DocumentModel();
- model.fromString('foo');
- expect(model.toString()).toBe('foo');
- });
- });
- describe('#toJSON()', () => {
- it('should serialize the model to JSON', () => {
- const model = new DocumentModel();
- const data = { foo: 1 };
- model.fromJSON(data);
- expect(model.toJSON()).toEqual(data);
- });
- });
- describe('#fromJSON()', () => {
- it('should deserialize the model from JSON', () => {
- const model = new DocumentModel();
- const data: null = null;
- model.fromJSON(data);
- expect(model.toString()).toBe('null');
- });
- });
- });
- describe('TextModelFactory', () => {
- describe('#name', () => {
- it('should get the name of the model type', () => {
- const factory = new TextModelFactory();
- expect(factory.name).toBe('text');
- });
- });
- describe('#contentType', () => {
- it('should get the file type', () => {
- const factory = new TextModelFactory();
- expect(factory.contentType).toBe('file');
- });
- });
- describe('#fileFormat', () => {
- it('should get the file format', () => {
- const factory = new TextModelFactory();
- expect(factory.fileFormat).toBe('text');
- });
- });
- describe('#isDisposed', () => {
- it('should get whether the factory is disposed', () => {
- const factory = new TextModelFactory();
- expect(factory.isDisposed).toBe(false);
- factory.dispose();
- expect(factory.isDisposed).toBe(true);
- });
- });
- describe('#dispose()', () => {
- it('should dispose of the resources held by the factory', () => {
- const factory = new TextModelFactory();
- factory.dispose();
- expect(factory.isDisposed).toBe(true);
- });
- it('should be safe to call multiple times', () => {
- const factory = new TextModelFactory();
- factory.dispose();
- factory.dispose();
- expect(factory.isDisposed).toBe(true);
- });
- });
- describe('#createNew()', () => {
- it('should create a new model', () => {
- const factory = new TextModelFactory();
- const model = factory.createNew();
- expect(model).toBeInstanceOf(DocumentModel);
- });
- it('should accept a language preference', () => {
- const factory = new TextModelFactory();
- const model = factory.createNew('foo');
- expect(model.defaultKernelLanguage).toBe('foo');
- });
- });
- describe('#preferredLanguage()', () => {
- it('should get the preferred kernel language given an extension', () => {
- const factory = new TextModelFactory();
- expect(factory.preferredLanguage('.py')).toBe('python');
- expect(factory.preferredLanguage('.jl')).toBe('julia');
- });
- });
- });
- describe('DocumentWidget', () => {
- let manager: ServiceManager.IManager;
- let context: Context<DocumentRegistry.IModel>;
- let content: Widget;
- let widget: DocumentWidget;
- const setup = async () => {
- context = await Mock.createFileContext(false, manager);
- content = new Widget();
- widget = new DocumentWidget({ context, content });
- };
- beforeAll(async () => {
- manager = new Mock.ServiceManagerMock();
- await manager.ready;
- });
- describe('#constructor', () => {
- beforeEach(setup);
- it('should set the title for the path', () => {
- expect(widget.title.label).toBe(context.localPath);
- });
- it('should update the title when the path changes', async () => {
- const path = UUID.uuid4() + '.jl';
- await manager.contents.rename(context.path, path);
- expect(widget.title.label).toBe(path);
- });
- it('should add the dirty class when the model is dirty', async () => {
- context.model.fromString('bar');
- expect(widget.title.className).toContain('jp-mod-dirty');
- });
- it('should store the context', () => {
- expect(widget.context).toBe(context);
- });
- });
- describe('#revealed', () => {
- beforeEach(setup);
- it('should resolve after the reveal and context ready promises', async () => {
- const thisContext = new Context({
- manager,
- factory: new TextModelFactory(),
- path: UUID.uuid4()
- });
- const x = Object.create(null);
- const reveal = sleep(300, x);
- const contextReady = Promise.all([thisContext.ready, x]);
- const widget = new DocumentWidget({
- context: thisContext,
- content,
- reveal
- });
- expect(widget.isRevealed).toBe(false);
- // Our promise should resolve before the widget reveal promise.
- expect(await Promise.race([widget.revealed, reveal])).toBe(x);
- // The context ready promise should also resolve first.
- void thisContext.initialize(true);
- expect(await Promise.race([widget.revealed, contextReady])).toEqual([
- undefined,
- x
- ]);
- // The widget.revealed promise should finally resolve.
- expect(await widget.revealed).toBeUndefined();
- thisContext.dispose();
- });
- });
- });
- });
|