Browse Source

modernize docregistry tests

fix error message

allow for flaky tests with jest-retries

don't link private packages in dev mode

fix exclude matrix

remove storybook files

cleanup services tests

fix ikernel test
Steven Silvester 4 years ago
parent
commit
f32a132133

+ 1 - 1
.github/workflows/linuxtests.yml

@@ -16,7 +16,7 @@ jobs:
             python: 3.5
           - group: docs
             python: 3.5
-          - group: docs
+          - group: docs2
             python: 3.5
           - group: changelog
             python: 3.5

+ 4 - 0
buildutils/src/ensure-repo.ts

@@ -240,6 +240,10 @@ function ensureJupyterlab(): string[] {
     } catch (e) {
       return;
     }
+    // Skip private packages.
+    if (data.private === true) {
+      return;
+    }
 
     // watch all src, build, and test files in the Jupyterlab project
     const relativePath = utils.ensureUnixPathSep(

+ 1 - 21
dev_mode/package.json

@@ -267,13 +267,6 @@
       "react-dom"
     ],
     "linkedPackages": {
-      "@jupyterlab/application-top": "../dev_mode",
-      "@jupyterlab/example-app": "../examples/app",
-      "@jupyterlab/example-cell": "../examples/cell",
-      "@jupyterlab/example-console": "../examples/console",
-      "@jupyterlab/example-filebrowser": "../examples/filebrowser",
-      "@jupyterlab/example-notebook": "../examples/notebook",
-      "@jupyterlab/example-terminal": "../examples/terminal",
       "@jupyterlab/application": "../packages/application",
       "@jupyterlab/application-extension": "../packages/application-extension",
       "@jupyterlab/apputils": "../packages/apputils",
@@ -357,22 +350,9 @@
       "@jupyterlab/vdom": "../packages/vdom",
       "@jupyterlab/vdom-extension": "../packages/vdom-extension",
       "@jupyterlab/vega5-extension": "../packages/vega5-extension",
-      "node-example": "../packages/services/examples/node",
-      "@jupyterlab/example-services-browser": "../packages/services/examples/browser",
-      "@jupyterlab/example-services-outputarea": "../packages/services/examples/typescript-browser-with-output",
       "@jupyterlab/buildutils": "../buildutils",
       "@jupyterlab/template": "../buildutils/template",
-      "template-for-tests": "../buildutils/test-template",
-      "@jupyterlab/test-root": "../tests",
-      "@jupyterlab/test-apputils": "../tests/test-apputils",
-      "@jupyterlab/test-cells": "../tests/test-cells",
-      "@jupyterlab/test-docregistry": "../tests/test-docregistry",
-      "@jupyterlab/test-logconsole": "../tests/test-logconsole",
-      "@jupyterlab/test-notebook": "../tests/test-notebook",
-      "@jupyterlab/test-outputarea": "../tests/test-outputarea",
-      "@jupyterlab/test-terminal": "../tests/test-terminal",
-      "@jupyterlab/testutils": "../testutils",
-      "@jupyterlab/mock-extension": "../jupyterlab/tests/mock_packages/extension"
+      "@jupyterlab/testutils": "../testutils"
     }
   }
 }

+ 11 - 0
packages/docregistry/.vscode/launch.json

@@ -0,0 +1,11 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "node",
+            "request": "attach",
+            "name": "Attach",
+            "port": 9229
+        }
+    ]
+}

+ 0 - 0
tests/test-docregistry/babel.config.js → packages/docregistry/babel.config.js


+ 2 - 0
packages/docregistry/jest.config.js

@@ -0,0 +1,2 @@
+const func = require('@jupyterlab/testutils/lib/jest-config-new');
+module.exports = func(__dirname);

+ 9 - 0
packages/docregistry/package.json

@@ -29,9 +29,14 @@
   },
   "scripts": {
     "build": "tsc -b",
+    "build:test": "tsc --build tsconfig.test.json",
     "clean": "rimraf lib",
     "docs": "typedoc src",
     "prepublishOnly": "npm run build",
+    "test": "jest",
+    "test:cov": "jest --collect-coverage",
+    "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
+    "test:debug:watch": "node --inspect-brk node_modules/.bin/jest --runInBand --watch",
     "watch": "tsc -b --watch"
   },
   "dependencies": {
@@ -52,7 +57,11 @@
     "@lumino/widgets": "^1.11.1"
   },
   "devDependencies": {
+    "@jupyterlab/testutils": "^2.1.0",
+    "@types/jest": "^24.0.23",
+    "jest": "^25.2.3",
     "rimraf": "~3.0.0",
+    "ts-jest": "^25.2.1",
     "typedoc": "^0.15.4",
     "typescript": "~3.7.3"
   },

+ 63 - 64
tests/test-docregistry/src/context.spec.ts → packages/docregistry/test/context.spec.ts

@@ -1,7 +1,6 @@
 // Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
 
-import { expect } from 'chai';
+import 'jest';
 
 import { UUID } from '@lumino/coreutils';
 
@@ -26,12 +25,14 @@ import {
 } from '@jupyterlab/testutils';
 import { SessionContext } from '@jupyterlab/apputils';
 
+import * as Mock from '@jupyterlab/testutils/lib/mock';
+
 describe('docregistry/context', () => {
   let manager: ServiceManager.IManager;
   const factory = new TextModelFactory();
 
   beforeAll(() => {
-    manager = new ServiceManager({ standby: 'never' });
+    manager = new Mock.ServiceManagerMock();
     return manager.ready;
   });
 
@@ -58,7 +59,7 @@ describe('docregistry/context', () => {
           factory,
           path: UUID.uuid4() + '.txt'
         });
-        expect(context).to.be.an.instanceof(Context);
+        expect(context).toBeInstanceOf(Context);
       });
     });
 
@@ -67,13 +68,13 @@ describe('docregistry/context', () => {
         const newPath = UUID.uuid4() + '.txt';
         let called = false;
         context.pathChanged.connect((sender, args) => {
-          expect(sender).to.equal(context);
-          expect(args).to.equal(newPath);
+          expect(sender).toBe(context);
+          expect(args).toBe(newPath);
           called = true;
         });
         await context.initialize(true);
         await manager.contents.rename(context.path, newPath);
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
       });
     });
 
@@ -82,12 +83,12 @@ describe('docregistry/context', () => {
         const path = context.path;
         let called = false;
         context.fileChanged.connect((sender, args) => {
-          expect(sender).to.equal(context);
-          expect(args.path).to.equal(path);
+          expect(sender).toBe(context);
+          expect(args.path).toBe(path);
           called = true;
         });
         await context.initialize(true);
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
       });
     });
 
@@ -97,8 +98,8 @@ describe('docregistry/context', () => {
         let checked = false;
         context.saveState.connect((sender, args) => {
           if (!called) {
-            expect(sender).to.equal(context);
-            expect(args).to.equal('started');
+            expect(sender).toBe(context);
+            expect(args).toBe('started');
 
             checked = true;
           }
@@ -107,8 +108,8 @@ describe('docregistry/context', () => {
         });
 
         await context.initialize(true);
-        expect(called).to.be.true;
-        expect(checked).to.be.true;
+        expect(called).toBe(true);
+        expect(checked).toBe(true);
       });
 
       it("should emit 'completed' when the file ends saving", async () => {
@@ -116,8 +117,8 @@ describe('docregistry/context', () => {
         let checked = false;
         context.saveState.connect((sender, args) => {
           if (called > 0) {
-            expect(sender).to.equal(context);
-            expect(args).to.equal('completed');
+            expect(sender).toBe(context);
+            expect(args).toBe('completed');
             checked = true;
           }
 
@@ -125,22 +126,22 @@ describe('docregistry/context', () => {
         });
 
         await context.initialize(true);
-        expect(called).to.equal(2);
-        expect(checked).to.be.true;
+        expect(called).toBe(2);
+        expect(checked).toBe(true);
       });
 
       it("should emit 'failed' when the save operation fails out", async () => {
         context = new Context({
           manager,
           factory,
-          path: 'src/readonly-temp.txt'
+          path: 'readonly.txt'
         });
 
         let called = 0;
         let checked;
         context.saveState.connect((sender, args) => {
           if (called > 0) {
-            expect(sender).to.equal(context);
+            expect(sender).toBe(context);
             checked = args;
           }
 
@@ -150,13 +151,11 @@ describe('docregistry/context', () => {
         try {
           await context.initialize(true);
         } catch (err) {
-          expect(err.message).to.contain(
-            'Permission denied: src/readonly-temp.txt'
-          );
+          expect(err.message).toContain('Invalid response: 403 Forbidden');
         }
 
-        expect(called).to.equal(2);
-        expect(checked).to.equal('failed');
+        expect(called).toBe(2);
+        expect(checked).toBe('failed');
 
         await acceptDialog();
       });
@@ -164,10 +163,10 @@ describe('docregistry/context', () => {
 
     describe('#isReady', () => {
       it('should indicate whether the context is ready', async () => {
-        expect(context.isReady).to.equal(false);
+        expect(context.isReady).toBe(false);
         const func = async () => {
           await context.ready;
-          expect(context.isReady).to.equal(true);
+          expect(context.isReady).toBe(true);
         };
         const promise = func();
         await context.initialize(true);
@@ -192,26 +191,26 @@ describe('docregistry/context', () => {
       });
 
       it('should initialize the model when the file is saved for the first time', async () => {
-        const context = await initNotebookContext();
+        const context = await initNotebookContext({ manager });
         context.model.fromJSON(NBTestUtils.DEFAULT_CONTENT);
-        expect(context.model.cells.canUndo).to.equal(true);
+        expect(context.model.cells.canUndo).toBe(true);
         await context.initialize(true);
         await context.ready;
-        expect(context.model.cells.canUndo).to.equal(false);
+        expect(context.model.cells.canUndo).toBe(false);
       });
 
       it('should initialize the model when the file is reverted for the first time', async () => {
-        const context = await initNotebookContext();
+        const context = await initNotebookContext({ manager });
         await manager.contents.save(context.path, {
           type: 'notebook',
           format: 'json',
           content: NBTestUtils.DEFAULT_CONTENT
         });
         context.model.fromJSON(NBTestUtils.DEFAULT_CONTENT);
-        expect(context.model.cells.canUndo).to.equal(true);
+        expect(context.model.cells.canUndo).toBe(true);
         await context.initialize(false);
         await context.ready;
-        expect(context.model.cells.canUndo).to.equal(false);
+        expect(context.model.cells.canUndo).toBe(false);
       });
     });
 
@@ -219,36 +218,36 @@ describe('docregistry/context', () => {
       it('should be emitted when the context is disposed', () => {
         let called = false;
         context.disposed.connect((sender, args) => {
-          expect(sender).to.equal(context);
-          expect(args).to.be.undefined;
+          expect(sender).toBe(context);
+          expect(args).toBeUndefined();
           called = true;
         });
         context.dispose();
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
       });
     });
 
     describe('#model', () => {
       it('should be the model associated with the document', () => {
-        expect(context.model.toString()).to.equal('');
+        expect(context.model.toString()).toBe('');
       });
     });
 
     describe('#sessionContext', () => {
       it('should be a ISessionContext object', () => {
-        expect(context.sessionContext).to.be.instanceOf(SessionContext);
+        expect(context.sessionContext).toBeInstanceOf(SessionContext);
       });
     });
 
     describe('#path', () => {
       it('should be the current path for the context', () => {
-        expect(typeof context.path).to.equal('string');
+        expect(typeof context.path).toBe('string');
       });
     });
 
     describe('#contentsModel', () => {
       it('should be `null` before population', () => {
-        expect(context.contentsModel).to.be.null;
+        expect(context.contentsModel).toBeNull();
       });
 
       it('should be set after population', async () => {
@@ -256,30 +255,30 @@ describe('docregistry/context', () => {
 
         void context.initialize(true);
         await context.ready;
-        expect(context.contentsModel!.path).to.equal(path);
+        expect(context.contentsModel!.path).toBe(path);
       });
     });
 
     describe('#factoryName', () => {
       it('should be the name of the factory used by the context', () => {
-        expect(context.factoryName).to.equal(factory.name);
+        expect(context.factoryName).toBe(factory.name);
       });
     });
 
     describe('#isDisposed', () => {
       it('should test whether the context is disposed', () => {
-        expect(context.isDisposed).to.equal(false);
+        expect(context.isDisposed).toBe(false);
         context.dispose();
-        expect(context.isDisposed).to.equal(true);
+        expect(context.isDisposed).toBe(true);
       });
     });
 
     describe('#dispose()', () => {
       it('should dispose of the resources used by the context', () => {
         context.dispose();
-        expect(context.isDisposed).to.equal(true);
+        expect(context.isDisposed).toBe(true);
         context.dispose();
-        expect(context.isDisposed).to.equal(true);
+        expect(context.isDisposed).toBe(true);
       });
     });
 
@@ -296,7 +295,7 @@ describe('docregistry/context', () => {
         };
         const model = await manager.contents.get(context.path, opts);
 
-        expect(model.content).to.equal('foo');
+        expect(model.content).toBe('foo');
       });
 
       it('should should preserve LF line endings upon save', async () => {
@@ -314,7 +313,7 @@ describe('docregistry/context', () => {
           content: true
         };
         const model = await manager.contents.get(context.path, opts);
-        expect(model.content).to.equal('foo\nbar');
+        expect(model.content).toBe('foo\nbar');
       });
 
       it('should should preserve CRLF line endings upon save', async () => {
@@ -332,7 +331,7 @@ describe('docregistry/context', () => {
           content: true
         };
         const model = await manager.contents.get(context.path, opts);
-        expect(model.content).to.equal('foo\r\nbar');
+        expect(model.content).toBe('foo\r\nbar');
       });
     });
 
@@ -356,11 +355,11 @@ describe('docregistry/context', () => {
         const oldPath = context.path;
         await context.saveAs();
         await promise;
-        expect(context.path).to.equal(newPath);
+        expect(context.path).toBe(newPath);
         // Make sure the both files are there now.
         const model = await manager.contents.get('', { content: true });
-        expect(model.content.find((x: any) => x.name === oldPath)).to.be.ok;
-        expect(model.content.find((x: any) => x.name === newPath)).to.be.ok;
+        expect(model.content.find((x: any) => x.name === oldPath)).toBeTruthy();
+        expect(model.content.find((x: any) => x.name === newPath)).toBeTruthy();
       });
 
       it('should bring up a conflict dialog', async () => {
@@ -383,7 +382,7 @@ describe('docregistry/context', () => {
         const promise = func();
         await context.saveAs();
         await promise;
-        expect(context.path).to.equal(newPath);
+        expect(context.path).toBe(newPath);
       });
 
       it('should keep the file if overwrite is aborted', async () => {
@@ -406,7 +405,7 @@ describe('docregistry/context', () => {
         const promise = func();
         await context.saveAs();
         await promise;
-        expect(context.path).to.equal(oldPath);
+        expect(context.path).toBe(oldPath);
       });
 
       it('should just save if the file name does not change', async () => {
@@ -415,7 +414,7 @@ describe('docregistry/context', () => {
         const promise = context.saveAs();
         await acceptDialog();
         await promise;
-        expect(context.path).to.equal(path);
+        expect(context.path).toBe(path);
       });
     });
 
@@ -426,7 +425,7 @@ describe('docregistry/context', () => {
         await context.save();
         context.model.fromString('bar');
         await context.revert();
-        expect(context.model.toString()).to.equal('foo');
+        expect(context.model.toString()).toBe('foo');
       });
 
       it('should normalize CRLF line endings to LF', async () => {
@@ -437,7 +436,7 @@ describe('docregistry/context', () => {
           content: 'foo\r\nbar'
         });
         await context.revert();
-        expect(context.model.toString()).to.equal('foo\nbar');
+        expect(context.model.toString()).toBe('foo\nbar');
       });
     });
 
@@ -445,8 +444,8 @@ describe('docregistry/context', () => {
       it('should create a checkpoint for the file', async () => {
         await context.initialize(true);
         const model = await context.createCheckpoint();
-        expect(model.id).to.be.ok;
-        expect(model.last_modified).to.be.ok;
+        expect(model.id).toBeTruthy();
+        expect(model.last_modified).toBeTruthy();
       });
     });
 
@@ -456,7 +455,7 @@ describe('docregistry/context', () => {
         const model = await context.createCheckpoint();
         await context.deleteCheckpoint(model.id);
         const models = await context.listCheckpoints();
-        expect(models.length).to.equal(0);
+        expect(models.length).toBe(0);
       });
     });
 
@@ -470,7 +469,7 @@ describe('docregistry/context', () => {
         await context.save();
         await context.restoreCheckpoint(id);
         await context.revert();
-        expect(context.model.toString()).to.equal('bar');
+        expect(context.model.toString()).toBe('bar');
       });
     });
 
@@ -486,13 +485,13 @@ describe('docregistry/context', () => {
             found = true;
           }
         }
-        expect(found).to.equal(true);
+        expect(found).toBe(true);
       });
     });
 
     describe('#urlResolver', () => {
       it('should be a url resolver', () => {
-        expect(context.urlResolver).to.be.an.instanceof(
+        expect(context.urlResolver).toBeInstanceOf(
           RenderMimeRegistry.UrlResolver
         );
       });
@@ -511,7 +510,7 @@ describe('docregistry/context', () => {
           opener
         });
         context.addSibling(new Widget());
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
       });
     });
   });

+ 92 - 90
tests/test-docregistry/src/default.spec.ts → packages/docregistry/test/default.spec.ts

@@ -1,7 +1,6 @@
 // Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
 
-import { expect } from 'chai';
+import 'jest';
 
 import { toArray } from '@lumino/algorithm';
 
@@ -24,6 +23,8 @@ import { ServiceManager } from '@jupyterlab/services';
 
 import { createFileContext, sleep } from '@jupyterlab/testutils';
 
+import * as Mock from '@jupyterlab/testutils/lib/mock';
+
 class WidgetFactory extends ABCWidgetFactory<IDocumentWidget> {
   protected createNewWidget(
     context: DocumentRegistry.Context
@@ -50,7 +51,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.fileTypes).to.deep.equal(['text']);
+        expect(factory.fileTypes).toEqual(['text']);
       });
     });
 
@@ -60,7 +61,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.name).to.equal('test');
+        expect(factory.name).toBe('test');
       });
     });
 
@@ -70,7 +71,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.defaultFor).to.deep.equal([]);
+        expect(factory.defaultFor).toEqual([]);
       });
 
       it('should be the value passed in', () => {
@@ -79,7 +80,7 @@ describe('docregistry/default', () => {
           fileTypes: ['text'],
           defaultFor: ['text']
         });
-        expect(factory.defaultFor).to.deep.equal(['text']);
+        expect(factory.defaultFor).toEqual(['text']);
       });
     });
 
@@ -89,7 +90,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.defaultRendered).to.deep.equal([]);
+        expect(factory.defaultRendered).toEqual([]);
       });
 
       it('should be the value passed in', () => {
@@ -98,7 +99,7 @@ describe('docregistry/default', () => {
           fileTypes: ['text'],
           defaultRendered: ['text']
         });
-        expect(factory.defaultRendered).to.deep.equal(['text']);
+        expect(factory.defaultRendered).toEqual(['text']);
       });
     });
 
@@ -108,7 +109,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.readOnly).to.equal(false);
+        expect(factory.readOnly).toBe(false);
       });
 
       it('should be the value passed in', () => {
@@ -117,7 +118,7 @@ describe('docregistry/default', () => {
           fileTypes: ['text'],
           readOnly: true
         });
-        expect(factory.readOnly).to.equal(true);
+        expect(factory.readOnly).toBe(true);
       });
     });
 
@@ -127,7 +128,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.modelName).to.equal('text');
+        expect(factory.modelName).toBe('text');
       });
 
       it('should be the value passed in', () => {
@@ -136,7 +137,7 @@ describe('docregistry/default', () => {
           fileTypes: ['text'],
           modelName: 'notebook'
         });
-        expect(factory.modelName).to.equal('notebook');
+        expect(factory.modelName).toBe('notebook');
       });
     });
 
@@ -146,7 +147,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.preferKernel).to.equal(false);
+        expect(factory.preferKernel).toBe(false);
       });
 
       it('should be the value passed in', () => {
@@ -155,7 +156,7 @@ describe('docregistry/default', () => {
           fileTypes: ['text'],
           preferKernel: true
         });
-        expect(factory.preferKernel).to.equal(true);
+        expect(factory.preferKernel).toBe(true);
       });
     });
 
@@ -165,7 +166,7 @@ describe('docregistry/default', () => {
           name: 'test',
           fileTypes: ['text']
         });
-        expect(factory.canStartKernel).to.equal(false);
+        expect(factory.canStartKernel).toBe(false);
       });
 
       it('should be the value passed in', () => {
@@ -174,7 +175,7 @@ describe('docregistry/default', () => {
           fileTypes: ['text'],
           canStartKernel: true
         });
-        expect(factory.canStartKernel).to.equal(true);
+        expect(factory.canStartKernel).toBe(true);
       });
 
       it('should have toolbar items', () => {
@@ -195,19 +196,19 @@ describe('docregistry/default', () => {
         const context = createFileContext();
         const widget = factory.createNew(context);
         const widget2 = factory.createNew(context);
-        expect(toArray(widget.toolbar.names())).to.deep.equal(['foo', 'bar']);
-        expect(toArray(widget2.toolbar.names())).to.deep.equal(['foo', 'bar']);
-        expect(toArray(widget.toolbar.children()).length).to.equal(2);
-        expect(toArray(widget2.toolbar.children()).length).to.equal(2);
+        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).to.equal(false);
+        expect(factory.isDisposed).toBe(false);
         factory.dispose();
-        expect(factory.isDisposed).to.equal(true);
+        expect(factory.isDisposed).toBe(true);
       });
     });
 
@@ -215,14 +216,14 @@ describe('docregistry/default', () => {
       it('should dispose of the resources held by the factory', () => {
         const factory = createFactory();
         factory.dispose();
-        expect(factory.isDisposed).to.equal(true);
+        expect(factory.isDisposed).toBe(true);
       });
 
       it('should be safe to call multiple times', () => {
         const factory = createFactory();
         factory.dispose();
         factory.dispose();
-        expect(factory.isDisposed).to.equal(true);
+        expect(factory.isDisposed).toBe(true);
       });
     });
 
@@ -231,7 +232,7 @@ describe('docregistry/default', () => {
         const factory = createFactory();
         const context = createFileContext();
         const widget = factory.createNew(context);
-        expect(widget).to.be.an.instanceof(Widget);
+        expect(widget).toBeInstanceOf(Widget);
       });
 
       it('should take an optional source widget for cloning', () => {
@@ -242,9 +243,9 @@ describe('docregistry/default', () => {
           context,
           widget
         );
-        expect(clonedWidget).to.not.equal(widget);
-        expect(clonedWidget.hasClass('WidgetFactory')).to.be.true;
-        expect(clonedWidget.context).to.equal(widget.context);
+        expect(clonedWidget).not.toBe(widget);
+        expect(clonedWidget.hasClass('WidgetFactory')).toBe(true);
+        expect(clonedWidget.context).toBe(widget.context);
       });
     });
   });
@@ -253,21 +254,21 @@ describe('docregistry/default', () => {
     describe('#name', () => {
       it('should get the name of the model type', () => {
         const factory = new Base64ModelFactory();
-        expect(factory.name).to.equal('base64');
+        expect(factory.name).toBe('base64');
       });
     });
 
     describe('#contentType', () => {
       it('should get the file type', () => {
         const factory = new Base64ModelFactory();
-        expect(factory.contentType).to.equal('file');
+        expect(factory.contentType).toBe('file');
       });
     });
 
     describe('#fileFormat', () => {
       it('should get the file format', () => {
         const factory = new Base64ModelFactory();
-        expect(factory.fileFormat).to.equal('base64');
+        expect(factory.fileFormat).toBe('base64');
       });
     });
   });
@@ -276,21 +277,21 @@ describe('docregistry/default', () => {
     describe('#constructor()', () => {
       it('should create a new document model', () => {
         const model = new DocumentModel();
-        expect(model).to.be.an.instanceof(DocumentModel);
+        expect(model).toBeInstanceOf(DocumentModel);
       });
 
       it('should accept an optional language preference', () => {
         const model = new DocumentModel('foo');
-        expect(model.defaultKernelLanguage).to.equal('foo');
+        expect(model.defaultKernelLanguage).toBe('foo');
       });
     });
 
     describe('#isDisposed', () => {
       it('should get whether the model has been disposed', () => {
         const model = new DocumentModel();
-        expect(model.isDisposed).to.equal(false);
+        expect(model.isDisposed).toBe(false);
         model.dispose();
-        expect(model.isDisposed).to.equal(true);
+        expect(model.isDisposed).toBe(true);
       });
     });
 
@@ -299,12 +300,12 @@ describe('docregistry/default', () => {
         const model = new DocumentModel();
         let called = false;
         model.contentChanged.connect((sender, args) => {
-          expect(sender).to.equal(model);
-          expect(args).to.be.undefined;
+          expect(sender).toBe(model);
+          expect(args).toBeUndefined();
           called = true;
         });
         model.fromString('foo');
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
       });
 
       it('should not be emitted if the content does not change', () => {
@@ -314,7 +315,7 @@ describe('docregistry/default', () => {
           called = true;
         });
         model.fromString('');
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
       });
     });
 
@@ -323,14 +324,14 @@ describe('docregistry/default', () => {
         const model = new DocumentModel();
         let called = false;
         model.stateChanged.connect((sender, args) => {
-          expect(sender).to.equal(model);
-          expect(args.name).to.equal('readOnly');
-          expect(args.oldValue).to.equal(false);
-          expect(args.newValue).to.equal(true);
+          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).to.equal(true);
+        expect(called).toBe(true);
       });
 
       it('should not be emitted if the state does not change', () => {
@@ -340,28 +341,28 @@ describe('docregistry/default', () => {
           called = true;
         });
         model.dirty = false;
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
       });
     });
 
     describe('#dirty', () => {
       it('should get the dirty state of the document', () => {
         const model = new DocumentModel();
-        expect(model.dirty).to.equal(false);
+        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).to.equal(model);
-          expect(args.name).to.equal('dirty');
-          expect(args.oldValue).to.equal(false);
-          expect(args.newValue).to.equal(true);
+          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).to.equal(true);
+        expect(called).toBe(true);
       });
 
       it('should not emit `stateChanged` when not changed', () => {
@@ -371,28 +372,28 @@ describe('docregistry/default', () => {
           called = true;
         });
         model.dirty = false;
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
       });
     });
 
     describe('#readOnly', () => {
       it('should get the read only state of the document', () => {
         const model = new DocumentModel();
-        expect(model.readOnly).to.equal(false);
+        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).to.equal(model);
-          expect(args.name).to.equal('readOnly');
-          expect(args.oldValue).to.equal(false);
-          expect(args.newValue).to.equal(true);
+          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).to.equal(true);
+        expect(called).toBe(true);
       });
 
       it('should not emit `stateChanged` when not changed', () => {
@@ -402,26 +403,26 @@ describe('docregistry/default', () => {
           called = true;
         });
         model.readOnly = false;
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
       });
     });
 
     describe('#defaultKernelName', () => {
       it('should get the default kernel name of the document', () => {
         const model = new DocumentModel();
-        expect(model.defaultKernelName).to.equal('');
+        expect(model.defaultKernelName).toBe('');
       });
     });
 
     describe('defaultKernelLanguage', () => {
       it('should get the default kernel language of the document', () => {
         const model = new DocumentModel();
-        expect(model.defaultKernelLanguage).to.equal('');
+        expect(model.defaultKernelLanguage).toBe('');
       });
 
       it('should be set by the constructor arg', () => {
         const model = new DocumentModel('foo');
-        expect(model.defaultKernelLanguage).to.equal('foo');
+        expect(model.defaultKernelLanguage).toBe('foo');
       });
     });
 
@@ -429,21 +430,21 @@ describe('docregistry/default', () => {
       it('should dispose of the resources held by the document manager', () => {
         const model = new DocumentModel();
         model.dispose();
-        expect(model.isDisposed).to.equal(true);
+        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).to.equal(true);
+        expect(model.isDisposed).toBe(true);
       });
     });
 
     describe('#toString()', () => {
       it('should serialize the model to a string', () => {
         const model = new DocumentModel();
-        expect(model.toString()).to.equal('');
+        expect(model.toString()).toBe('');
       });
     });
 
@@ -451,7 +452,7 @@ describe('docregistry/default', () => {
       it('should deserialize the model from a string', () => {
         const model = new DocumentModel();
         model.fromString('foo');
-        expect(model.toString()).to.equal('foo');
+        expect(model.toString()).toBe('foo');
       });
     });
 
@@ -460,7 +461,7 @@ describe('docregistry/default', () => {
         const model = new DocumentModel();
         const data = { foo: 1 };
         model.fromJSON(data);
-        expect(model.toJSON()).to.deep.equal(data);
+        expect(model.toJSON()).toEqual(data);
       });
     });
 
@@ -469,7 +470,7 @@ describe('docregistry/default', () => {
         const model = new DocumentModel();
         const data: null = null;
         model.fromJSON(data);
-        expect(model.toString()).to.equal('null');
+        expect(model.toString()).toBe('null');
       });
     });
   });
@@ -478,30 +479,30 @@ describe('docregistry/default', () => {
     describe('#name', () => {
       it('should get the name of the model type', () => {
         const factory = new TextModelFactory();
-        expect(factory.name).to.equal('text');
+        expect(factory.name).toBe('text');
       });
     });
 
     describe('#contentType', () => {
       it('should get the file type', () => {
         const factory = new TextModelFactory();
-        expect(factory.contentType).to.equal('file');
+        expect(factory.contentType).toBe('file');
       });
     });
 
     describe('#fileFormat', () => {
       it('should get the file format', () => {
         const factory = new TextModelFactory();
-        expect(factory.fileFormat).to.equal('text');
+        expect(factory.fileFormat).toBe('text');
       });
     });
 
     describe('#isDisposed', () => {
       it('should get whether the factory is disposed', () => {
         const factory = new TextModelFactory();
-        expect(factory.isDisposed).to.equal(false);
+        expect(factory.isDisposed).toBe(false);
         factory.dispose();
-        expect(factory.isDisposed).to.equal(true);
+        expect(factory.isDisposed).toBe(true);
       });
     });
 
@@ -509,14 +510,14 @@ describe('docregistry/default', () => {
       it('should dispose of the resources held by the factory', () => {
         const factory = new TextModelFactory();
         factory.dispose();
-        expect(factory.isDisposed).to.equal(true);
+        expect(factory.isDisposed).toBe(true);
       });
 
       it('should be safe to call multiple times', () => {
         const factory = new TextModelFactory();
         factory.dispose();
         factory.dispose();
-        expect(factory.isDisposed).to.equal(true);
+        expect(factory.isDisposed).toBe(true);
       });
     });
 
@@ -524,21 +525,21 @@ describe('docregistry/default', () => {
       it('should create a new model', () => {
         const factory = new TextModelFactory();
         const model = factory.createNew();
-        expect(model).to.be.an.instanceof(DocumentModel);
+        expect(model).toBeInstanceOf(DocumentModel);
       });
 
       it('should accept a language preference', () => {
         const factory = new TextModelFactory();
         const model = factory.createNew('foo');
-        expect(model.defaultKernelLanguage).to.equal('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')).to.equal('python');
-        expect(factory.preferredLanguage('.jl')).to.equal('julia');
+        expect(factory.preferredLanguage('.py')).toBe('python');
+        expect(factory.preferredLanguage('.jl')).toBe('julia');
       });
     });
   });
@@ -557,7 +558,7 @@ describe('docregistry/default', () => {
     };
 
     beforeAll(async () => {
-      manager = new ServiceManager({ standby: 'never' });
+      manager = new Mock.ServiceManagerMock();
       await manager.ready;
     });
 
@@ -565,25 +566,25 @@ describe('docregistry/default', () => {
       beforeEach(setup);
 
       it('should set the title for the path', () => {
-        expect(widget.title.label).to.equal(context.localPath);
+        expect(widget.title.label).toBe(context.localPath);
       });
 
       it('should update the title when the path changes', async () => {
         const path = UUID.uuid4() + '.jl';
         await context.initialize(true);
         await manager.contents.rename(context.path, path);
-        expect(widget.title.label).to.equal(path);
+        expect(widget.title.label).toBe(path);
       });
 
       it('should add the dirty class when the model is dirty', async () => {
         await context.initialize(true);
         await context.ready;
         context.model.fromString('bar');
-        expect(widget.title.className).to.contain('jp-mod-dirty');
+        expect(widget.title.className).toContain('jp-mod-dirty');
       });
 
       it('should store the context', () => {
-        expect(widget.context).to.equal(context);
+        expect(widget.context).toBe(context);
       });
     });
 
@@ -595,17 +596,18 @@ describe('docregistry/default', () => {
         const reveal = sleep(300, x);
         const contextReady = Promise.all([context.ready, x]);
         const widget = new DocumentWidget({ context, content, reveal });
-        expect(widget.isRevealed).to.equal(false);
+        expect(widget.isRevealed).toBe(false);
 
         // Our promise should resolve before the widget reveal promise.
-        expect(await Promise.race([widget.revealed, reveal])).to.equal(x);
+        expect(await Promise.race([widget.revealed, reveal])).toBe(x);
         // The context ready promise should also resolve first.
         void context.initialize(true);
-        expect(
-          await Promise.race([widget.revealed, contextReady])
-        ).to.deep.equal([undefined, x]);
+        expect(await Promise.race([widget.revealed, contextReady])).toEqual([
+          undefined,
+          x
+        ]);
         // The widget.revealed promise should finally resolve.
-        expect(await widget.revealed).to.be.undefined;
+        expect(await widget.revealed).toBeUndefined();
       });
     });
   });

+ 9 - 14
tests/test-docregistry/src/mimedocument.spec.ts → packages/docregistry/test/mimedocument.spec.ts

@@ -1,7 +1,6 @@
 // Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
 
-import { expect } from 'chai';
+import 'jest';
 
 import { Message } from '@lumino/messaging';
 
@@ -17,11 +16,9 @@ import {
 
 import { RenderedText, IRenderMime } from '@jupyterlab/rendermime';
 
-import {
-  createFileContext,
-  defaultRenderMime,
-  testEmission
-} from '@jupyterlab/testutils';
+import { defaultRenderMime, testEmission } from '@jupyterlab/testutils';
+
+import * as Mock from '@jupyterlab/testutils/lib/mock';
 
 const RENDERMIME = defaultRenderMime();
 
@@ -56,7 +53,7 @@ describe('docregistry/mimedocument', () => {
   let dContext: Context<DocumentRegistry.IModel>;
 
   beforeEach(() => {
-    dContext = createFileContext();
+    dContext = Mock.createFileContext();
   });
 
   afterEach(() => {
@@ -72,9 +69,7 @@ describe('docregistry/mimedocument', () => {
           rendermime: RENDERMIME,
           primaryFileType: DocumentRegistry.defaultTextFileType
         });
-        expect(widgetFactory.createNew(dContext)).to.be.an.instanceof(
-          MimeDocument
-        );
+        expect(widgetFactory.createNew(dContext)).toBeInstanceOf(MimeDocument);
       });
     });
   });
@@ -90,7 +85,7 @@ describe('docregistry/mimedocument', () => {
           renderTimeout: 1000,
           dataType: 'string'
         });
-        expect(widget).to.be.an.instanceof(MimeContent);
+        expect(widget).toBeInstanceOf(MimeContent);
       });
     });
 
@@ -107,7 +102,7 @@ describe('docregistry/mimedocument', () => {
         void dContext.initialize(true);
         await widget.ready;
         const layout = widget.layout as BoxLayout;
-        expect(layout.widgets.length).to.equal(1);
+        expect(layout.widgets.length).toBe(1);
       });
     });
 
@@ -117,7 +112,7 @@ describe('docregistry/mimedocument', () => {
         await dContext.initialize(true);
         const emission = testEmission(dContext.model.contentChanged, {
           test: () => {
-            expect(dContext.model.toString()).to.equal('bar');
+            expect(dContext.model.toString()).toBe('bar');
           }
         });
         const renderer = RENDERMIME.createRenderer('text/foo');

+ 82 - 91
tests/test-docregistry/src/registry.spec.ts → packages/docregistry/test/registry.spec.ts

@@ -1,7 +1,6 @@
 // Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
 
-import { expect } from 'chai';
+import 'jest';
 
 import { UUID } from '@lumino/coreutils';
 
@@ -68,9 +67,9 @@ describe('docregistry/registry', () => {
 
     describe('#isDisposed', () => {
       it('should get whether the registry has been disposed', () => {
-        expect(registry.isDisposed).to.equal(false);
+        expect(registry.isDisposed).toBe(false);
         registry.dispose();
-        expect(registry.isDisposed).to.equal(true);
+        expect(registry.isDisposed).toBe(true);
       });
     });
 
@@ -78,13 +77,13 @@ describe('docregistry/registry', () => {
       it('should dispose of the resources held by the registry', () => {
         registry.addFileType({ name: 'notebook', extensions: ['.ipynb'] });
         registry.dispose();
-        expect(registry.isDisposed).to.equal(true);
+        expect(registry.isDisposed).toBe(true);
       });
 
       it('should be safe to call multiple times', () => {
         registry.dispose();
         registry.dispose();
-        expect(registry.isDisposed).to.equal(true);
+        expect(registry.isDisposed).toBe(true);
       });
     });
 
@@ -92,8 +91,8 @@ describe('docregistry/registry', () => {
       it('should add the widget factory to the registry', () => {
         const factory = createFactory();
         registry.addWidgetFactory(factory);
-        expect(registry.getWidgetFactory(factory.name)).to.equal(factory);
-        expect(registry.getWidgetFactory(factory.name.toUpperCase())).to.equal(
+        expect(registry.getWidgetFactory(factory.name)).toBe(factory);
+        expect(registry.getWidgetFactory(factory.name.toUpperCase())).toBe(
           factory
         );
       });
@@ -105,7 +104,7 @@ describe('docregistry/registry', () => {
           defaultFor: ['*']
         });
         registry.addWidgetFactory(factory);
-        expect(registry.defaultWidgetFactory('*').name).to.equal('global');
+        expect(registry.defaultWidgetFactory('*').name).toBe('global');
       });
 
       it('should override an existing global default', () => {
@@ -122,21 +121,21 @@ describe('docregistry/registry', () => {
           defaultFor: ['*']
         });
         registry.addWidgetFactory(factory);
-        expect(registry.defaultWidgetFactory('*').name).to.equal('bar');
+        expect(registry.defaultWidgetFactory('*').name).toBe('bar');
       });
 
       it('should override an existing extension default', () => {
         registry.addWidgetFactory(createFactory());
         const factory = createFactory();
         registry.addWidgetFactory(factory);
-        expect(registry.defaultWidgetFactory('a.foo.bar')).to.equal(factory);
+        expect(registry.defaultWidgetFactory('a.foo.bar')).toBe(factory);
       });
 
       it('should be removed from the registry when disposed', () => {
         const factory = createFactory();
         const disposable = registry.addWidgetFactory(factory);
         disposable.dispose();
-        expect(registry.getWidgetFactory('test')).to.be.undefined;
+        expect(registry.getWidgetFactory('test')).toBeUndefined();
       });
 
       it('should throw for an invalid factory name', () => {
@@ -148,7 +147,7 @@ describe('docregistry/registry', () => {
               defaultFor: []
             })
           );
-        }).to.throw(/Invalid/);
+        }).toThrowError(/Invalid/);
         expect(() => {
           registry.addWidgetFactory(
             new WidgetFactory({
@@ -157,7 +156,7 @@ describe('docregistry/registry', () => {
               defaultFor: []
             })
           );
-        }).to.throw(/Invalid/);
+        }).toThrowError(/Invalid/);
       });
     });
 
@@ -192,7 +191,7 @@ describe('docregistry/registry', () => {
       it('should add a widget extension to the registry', () => {
         const extension = new WidgetExtension();
         registry.addWidgetExtension('foo', extension);
-        expect(registry.widgetExtensions('foo').next()).to.equal(extension);
+        expect(registry.widgetExtensions('foo').next()).toBe(extension);
       });
 
       it('should be a no-op if the extension is already registered for a given widget factory', () => {
@@ -200,14 +199,14 @@ describe('docregistry/registry', () => {
         registry.addWidgetExtension('foo', extension);
         const disposable = registry.addWidgetExtension('foo', extension);
         disposable.dispose();
-        expect(registry.widgetExtensions('foo').next()).to.equal(extension);
+        expect(registry.widgetExtensions('foo').next()).toBe(extension);
       });
 
       it('should be removed from the registry when disposed', () => {
         const extension = new WidgetExtension();
         const disposable = registry.addWidgetExtension('foo', extension);
         disposable.dispose();
-        expect(toArray(registry.widgetExtensions('foo')).length).to.equal(0);
+        expect(toArray(registry.widgetExtensions('foo')).length).toBe(0);
       });
     });
 
@@ -216,7 +215,7 @@ describe('docregistry/registry', () => {
         registry = new DocumentRegistry({ initialFileTypes: [] });
         const fileType = { name: 'notebook', extensions: ['.ipynb'] };
         registry.addFileType(fileType);
-        expect(registry.fileTypes().next()!.name).to.equal(fileType.name);
+        expect(registry.fileTypes().next()!.name).toBe(fileType.name);
       });
 
       it('should be removed from the registry when disposed', () => {
@@ -224,7 +223,7 @@ describe('docregistry/registry', () => {
         const fileType = { name: 'notebook', extensions: ['.ipynb'] };
         const disposable = registry.addFileType(fileType);
         disposable.dispose();
-        expect(toArray(registry.fileTypes()).length).to.equal(0);
+        expect(toArray(registry.fileTypes()).length).toBe(0);
       });
 
       it('should be a no-op if a file type of the same name is registered', () => {
@@ -233,7 +232,7 @@ describe('docregistry/registry', () => {
         registry.addFileType(fileType);
         const disposable = registry.addFileType(fileType);
         disposable.dispose();
-        expect(registry.fileTypes().next()!.name).to.equal(fileType.name);
+        expect(registry.fileTypes().next()!.name).toBe(fileType.name);
       });
     });
 
@@ -246,9 +245,9 @@ describe('docregistry/registry', () => {
       });
 
       it('should give the valid registered widget factories', () => {
-        expect(
-          toArray(registry.preferredWidgetFactories('foo.txt'))
-        ).to.deep.equal([]);
+        expect(toArray(registry.preferredWidgetFactories('foo.txt'))).toEqual(
+          []
+        );
         const factory = createFactory();
         registry.addWidgetFactory(factory);
         const gFactory = new WidgetFactory({
@@ -258,14 +257,14 @@ describe('docregistry/registry', () => {
         });
         registry.addWidgetFactory(gFactory);
         const factories = registry.preferredWidgetFactories('a.foo.bar');
-        expect(toArray(factories)).to.deep.equal([factory, gFactory]);
+        expect(toArray(factories)).toEqual([factory, gFactory]);
       });
 
       it('should not list a factory whose model is not registered', () => {
         registry.addWidgetFactory(createFactory('foobar'));
-        expect(
-          registry.preferredWidgetFactories('a.foo.bar').length
-        ).to.deep.equal(0);
+        expect(registry.preferredWidgetFactories('a.foo.bar').length).toEqual(
+          0
+        );
       });
 
       it('should select the factory for a given extension', () => {
@@ -276,10 +275,8 @@ describe('docregistry/registry', () => {
           fileTypes: ['markdown']
         });
         registry.addWidgetFactory(mdFactory);
-        expect(registry.preferredWidgetFactories('a.txt')[0]).to.equal(factory);
-        expect(registry.preferredWidgetFactories('a.md')[0]).to.equal(
-          mdFactory
-        );
+        expect(registry.preferredWidgetFactories('a.txt')[0]).toBe(factory);
+        expect(registry.preferredWidgetFactories('a.md')[0]).toBe(mdFactory);
       });
 
       it('should respect the priority order', () => {
@@ -297,7 +294,7 @@ describe('docregistry/registry', () => {
         });
         registry.addWidgetFactory(mdFactory);
         const factories = registry.preferredWidgetFactories('a.txt');
-        expect(toArray(factories)).to.deep.equal([factory, gFactory]);
+        expect(toArray(factories)).toEqual([factory, gFactory]);
       });
 
       it('should list a default rendered factory after the default factory', () => {
@@ -317,7 +314,7 @@ describe('docregistry/registry', () => {
         registry.addWidgetFactory(mdFactory);
 
         const factories = registry.preferredWidgetFactories('a.md');
-        expect(factories).to.deep.equal([mdFactory, gFactory]);
+        expect(factories).toEqual([mdFactory, gFactory]);
       });
 
       it('should handle multi-part extensions', () => {
@@ -334,9 +331,9 @@ describe('docregistry/registry', () => {
         });
         registry.addWidgetFactory(jFactory);
         let factories = registry.preferredWidgetFactories('foo.table.json');
-        expect(toArray(factories)).to.deep.equal([tFactory, jFactory]);
+        expect(toArray(factories)).toEqual([tFactory, jFactory]);
         factories = registry.preferredWidgetFactories('foo.json');
-        expect(toArray(factories)).to.deep.equal([jFactory]);
+        expect(toArray(factories)).toEqual([jFactory]);
       });
 
       it('should handle just a multi-part extension', () => {
@@ -346,9 +343,9 @@ describe('docregistry/registry', () => {
         });
         registry.addWidgetFactory(factory);
         let factories = registry.preferredWidgetFactories('foo.table.json');
-        expect(toArray(factories)).to.deep.equal([factory]);
+        expect(toArray(factories)).toEqual([factory]);
         factories = registry.preferredWidgetFactories('foo.json');
-        expect(toArray(factories)).to.deep.equal([]);
+        expect(toArray(factories)).toEqual([]);
       });
     });
 
@@ -368,9 +365,9 @@ describe('docregistry/registry', () => {
           defaultFor: ['markdown']
         });
         registry.addWidgetFactory(mdFactory);
-        expect(registry.defaultWidgetFactory('a.foo.bar')).to.equal(factory);
-        expect(registry.defaultWidgetFactory('a.md')).to.equal(mdFactory);
-        expect(registry.defaultWidgetFactory()).to.equal(gFactory);
+        expect(registry.defaultWidgetFactory('a.foo.bar')).toBe(factory);
+        expect(registry.defaultWidgetFactory('a.md')).toBe(mdFactory);
+        expect(registry.defaultWidgetFactory()).toBe(gFactory);
       });
     });
 
@@ -383,7 +380,7 @@ describe('docregistry/registry', () => {
         });
         registry.addWidgetFactory(mdFactory);
         registry.setDefaultWidgetFactory('foobar', 'markdown');
-        expect(registry.defaultWidgetFactory('a.foo.bar')).to.equal(mdFactory);
+        expect(registry.defaultWidgetFactory('a.foo.bar')).toBe(mdFactory);
       });
 
       it('should revert to the default widget factory when unset', () => {
@@ -397,7 +394,7 @@ describe('docregistry/registry', () => {
         registry.addWidgetFactory(mdFactory);
         registry.setDefaultWidgetFactory('foobar', 'markdown');
         registry.setDefaultWidgetFactory('foobar', undefined);
-        expect(registry.defaultWidgetFactory('a.foo.bar')).to.equal(factory);
+        expect(registry.defaultWidgetFactory('a.foo.bar')).toBe(factory);
       });
 
       it('should throw if the factory or file type do not exist', () => {
@@ -405,10 +402,10 @@ describe('docregistry/registry', () => {
         registry.addWidgetFactory(factory);
         expect(() => {
           registry.setDefaultWidgetFactory('foobar', 'fake');
-        }).to.throw(/Cannot find/);
+        }).toThrowError(/Cannot find/);
         expect(() => {
           registry.setDefaultWidgetFactory('fake', undefined);
-        }).to.throw(/Cannot find/);
+        }).toThrowError(/Cannot find/);
       });
 
       it('should throw if the factory cannot render a file type', () => {
@@ -420,7 +417,7 @@ describe('docregistry/registry', () => {
         registry.addWidgetFactory(mdFactory);
         expect(() => {
           registry.setDefaultWidgetFactory('foobar', 'markdown');
-        }).to.throw(/cannot view/);
+        }).toThrowError(/cannot view/);
       });
 
       it('should revert to the default widget factory if the override is removed', () => {
@@ -434,7 +431,7 @@ describe('docregistry/registry', () => {
         const disposable = registry.addWidgetFactory(mdFactory);
         registry.setDefaultWidgetFactory('foobar', 'markdown');
         disposable.dispose();
-        expect(registry.defaultWidgetFactory('a.foo.bar')).to.equal(factory);
+        expect(registry.defaultWidgetFactory('a.foo.bar')).toBe(factory);
       });
     });
 
@@ -448,12 +445,8 @@ describe('docregistry/registry', () => {
           defaultRendered: ['markdown']
         });
         registry.addWidgetFactory(mdFactory);
-        expect(registry.defaultRenderedWidgetFactory('a.baz')).to.equal(
-          factory
-        );
-        expect(registry.defaultRenderedWidgetFactory('a.md')).to.equal(
-          mdFactory
-        );
+        expect(registry.defaultRenderedWidgetFactory('a.baz')).toBe(factory);
+        expect(registry.defaultRenderedWidgetFactory('a.md')).toBe(mdFactory);
       });
 
       it('should get the default widget factory if no default rendered factory is registered', () => {
@@ -463,16 +456,14 @@ describe('docregistry/registry', () => {
           defaultFor: ['*']
         });
         registry.addWidgetFactory(gFactory);
-        expect(registry.defaultRenderedWidgetFactory('a.md')).to.equal(
-          gFactory
-        );
+        expect(registry.defaultRenderedWidgetFactory('a.md')).toBe(gFactory);
       });
     });
 
     describe('#fileTypes()', () => {
       it('should get the registered file types', () => {
         registry = new DocumentRegistry({ initialFileTypes: [] });
-        expect(toArray(registry.fileTypes()).length).to.equal(0);
+        expect(toArray(registry.fileTypes()).length).toBe(0);
         const fileTypes = [
           { name: 'notebook', extensions: ['.ipynb'] },
           { name: 'python', extensions: ['.py'] },
@@ -482,17 +473,17 @@ describe('docregistry/registry', () => {
         registry.addFileType(fileTypes[1]);
         registry.addFileType(fileTypes[2]);
         const values = registry.fileTypes();
-        expect(values.next()!.name).to.equal(fileTypes[0].name);
-        expect(values.next()!.name).to.equal(fileTypes[1].name);
-        expect(values.next()!.name).to.equal(fileTypes[2].name);
+        expect(values.next()!.name).toBe(fileTypes[0].name);
+        expect(values.next()!.name).toBe(fileTypes[1].name);
+        expect(values.next()!.name).toBe(fileTypes[2].name);
       });
     });
 
     describe('#getFileType()', () => {
       it('should get a file type by name', () => {
-        expect(registry.getFileType('notebook')).to.be.ok;
-        expect(registry.getFileType('python')).to.be.ok;
-        expect(registry.getFileType('fizzbuzz')).to.be.undefined;
+        expect(registry.getFileType('notebook')).toBeTruthy();
+        expect(registry.getFileType('python')).toBeTruthy();
+        expect(registry.getFileType('fizzbuzz')).toBeUndefined();
       });
     });
 
@@ -515,17 +506,17 @@ describe('docregistry/registry', () => {
           })
         );
         let pref = registry.getKernelPreference('.c', 'global');
-        expect(pref!.language).to.equal('clike');
-        expect(pref!.shouldStart).to.equal(false);
-        expect(pref!.canStart).to.equal(false);
+        expect(pref!.language).toBe('clike');
+        expect(pref!.shouldStart).toBe(false);
+        expect(pref!.canStart).toBe(false);
 
         pref = registry.getKernelPreference('.py', 'python');
-        expect(pref!.language).to.equal('python');
-        expect(pref!.shouldStart).to.equal(true);
-        expect(pref!.canStart).to.equal(true);
+        expect(pref!.language).toBe('python');
+        expect(pref!.shouldStart).toBe(true);
+        expect(pref!.canStart).toBe(true);
 
         pref = registry.getKernelPreference('.py', 'baz');
-        expect(pref).to.be.undefined;
+        expect(pref).toBeUndefined();
       });
     });
 
@@ -533,7 +524,7 @@ describe('docregistry/registry', () => {
       it('should get a registered model factory by name', () => {
         const mFactory = new Base64ModelFactory();
         registry.addModelFactory(mFactory);
-        expect(registry.getModelFactory('base64')).to.equal(mFactory);
+        expect(registry.getModelFactory('base64')).toBe(mFactory);
       });
     });
 
@@ -547,9 +538,9 @@ describe('docregistry/registry', () => {
           fileTypes: ['markdown']
         });
         registry.addWidgetFactory(mdFactory);
-        expect(registry.getWidgetFactory(factory.name)).to.equal(factory);
-        expect(registry.getWidgetFactory('markdown')).to.equal(mdFactory);
-        expect(registry.getWidgetFactory('baz')).to.be.undefined;
+        expect(registry.getWidgetFactory(factory.name)).toBe(factory);
+        expect(registry.getWidgetFactory('markdown')).toBe(mdFactory);
+        expect(registry.getWidgetFactory('baz')).toBeUndefined();
       });
     });
 
@@ -561,13 +552,13 @@ describe('docregistry/registry', () => {
         registry.addWidgetExtension('fizz', bar);
         registry.addWidgetExtension('buzz', foo);
         const fizz = toArray(registry.widgetExtensions('fizz'));
-        expect(fizz[0]).to.equal(foo);
-        expect(fizz[1]).to.equal(bar);
-        expect(fizz.length).to.equal(2);
+        expect(fizz[0]).toBe(foo);
+        expect(fizz[1]).toBe(bar);
+        expect(fizz.length).toBe(2);
         const buzz = toArray(registry.widgetExtensions('buzz'));
-        expect(buzz[0]).to.equal(foo);
-        expect(toArray(buzz).length).to.equal(1);
-        expect(registry.widgetExtensions('baz').next()).to.be.undefined;
+        expect(buzz[0]).toBe(foo);
+        expect(toArray(buzz).length).toBe(1);
+        expect(registry.widgetExtensions('baz').next()).toBeUndefined();
       });
     });
 
@@ -582,28 +573,28 @@ describe('docregistry/registry', () => {
         const ft = registry.getFileTypeForModel({
           type: 'directory'
         });
-        expect(ft.name).to.equal('directory');
+        expect(ft.name).toBe('directory');
       });
 
       it('should handle a notebook', () => {
         const ft = registry.getFileTypeForModel({
           type: 'notebook'
         });
-        expect(ft.name).to.equal('notebook');
+        expect(ft.name).toBe('notebook');
       });
 
       it('should handle a python file', () => {
         const ft = registry.getFileTypeForModel({
           name: 'foo.py'
         });
-        expect(ft.name).to.equal('python');
+        expect(ft.name).toBe('python');
       });
 
       it('should handle an unknown file', () => {
         const ft = registry.getFileTypeForModel({
           name: 'foo.bar'
         });
-        expect(ft.name).to.equal('text');
+        expect(ft.name).toBe('text');
       });
 
       it('should get the most specific extension', () => {
@@ -616,14 +607,14 @@ describe('docregistry/registry', () => {
         const ft = registry.getFileTypeForModel({
           name: 'foo.vg.json'
         });
-        expect(ft.name).to.equal('vega');
+        expect(ft.name).toBe('vega');
       });
 
       it('should be case insensitive', () => {
         const ft = registry.getFileTypeForModel({
           name: 'foo.PY'
         });
-        expect(ft.name).to.equal('python');
+        expect(ft.name).toBe('python');
       });
     });
 
@@ -636,17 +627,17 @@ describe('docregistry/registry', () => {
 
       it('should handle a notebook', () => {
         const ft = registry.getFileTypesForPath('foo/bar/baz.ipynb');
-        expect(ft[0].name).to.equal('notebook');
+        expect(ft[0].name).toBe('notebook');
       });
 
       it('should handle a python file', () => {
         const ft = registry.getFileTypesForPath('foo/bar/baz.py');
-        expect(ft[0].name).to.equal('python');
+        expect(ft[0].name).toBe('python');
       });
 
       it('should return an empty list for an unknown file', () => {
         const ft = registry.getFileTypesForPath('foo/bar/baz.weird');
-        expect(ft.length).to.equal(0);
+        expect(ft.length).toBe(0);
       });
 
       it('should get the most specific extension first', () => {
@@ -657,13 +648,13 @@ describe('docregistry/registry', () => {
           registry.addFileType(ft);
         });
         const ft = registry.getFileTypesForPath('foo/bar/baz.vg.json');
-        expect(ft[0].name).to.equal('vega');
-        expect(ft[1].name).to.equal('json');
+        expect(ft[0].name).toBe('vega');
+        expect(ft[1].name).toBe('json');
       });
 
       it('should be case insensitive', () => {
         const ft = registry.getFileTypesForPath('foo/bar/baz.PY');
-        expect(ft[0].name).to.equal('python');
+        expect(ft[0].name).toBe('python');
       });
     });
   });

+ 63 - 0
packages/docregistry/tsconfig.test.json

@@ -0,0 +1,63 @@
+{
+  "extends": "../../tsconfigbase.test",
+  "include": ["src/*", "test/*"],
+  "references": [
+    {
+      "path": "../apputils"
+    },
+    {
+      "path": "../codeeditor"
+    },
+    {
+      "path": "../codemirror"
+    },
+    {
+      "path": "../coreutils"
+    },
+    {
+      "path": "../observables"
+    },
+    {
+      "path": "../rendermime"
+    },
+    {
+      "path": "../rendermime-interfaces"
+    },
+    {
+      "path": "../services"
+    },
+    {
+      "path": "../ui-components"
+    },
+    {
+      "path": "../../testutils"
+    },
+    {
+      "path": "../apputils"
+    },
+    {
+      "path": "../codeeditor"
+    },
+    {
+      "path": "../codemirror"
+    },
+    {
+      "path": "../coreutils"
+    },
+    {
+      "path": "../observables"
+    },
+    {
+      "path": "../rendermime"
+    },
+    {
+      "path": "../rendermime-interfaces"
+    },
+    {
+      "path": "../services"
+    },
+    {
+      "path": "../ui-components"
+    }
+  ]
+}

+ 1 - 0
packages/services/package.json

@@ -66,6 +66,7 @@
     "@types/text-encoding": "^0.0.35",
     "@types/ws": "^7.2.4",
     "jest": "^25.2.3",
+    "jest-retries": "^1.0.1",
     "rimraf": "~3.0.0",
     "text-encoding": "^0.7.0",
     "ts-jest": "^25.2.1",

+ 2 - 0
packages/services/test/kernel/comm.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { PromiseDelegate } from '@lumino/coreutils';
 
 import { KernelMessage, Kernel, KernelManager } from '../../src';

+ 2 - 0
packages/services/test/kernel/ifuture.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { Kernel, KernelMessage, KernelAPI, KernelManager } from '../../src';
 
 import { KernelTester } from '../utils';

+ 6 - 2
packages/services/test/kernel/ikernel.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { PageConfig } from '@jupyterlab/coreutils';
 
 import { UUID } from '@lumino/coreutils';
@@ -41,7 +43,7 @@ describe('Kernel.IKernel', () => {
   let kernelManager: KernelManager;
 
   beforeAll(async () => {
-    jest.setTimeout(120000);
+    jest.setTimeout(20000);
     kernelManager = new KernelManager();
     specs = await KernelSpecAPI.getSpecs();
   });
@@ -62,7 +64,8 @@ describe('Kernel.IKernel', () => {
   });
 
   describe('#disposed', () => {
-    it('should be emitted when the kernel is disposed', () => {
+    it('should be emitted when the kernel is disposed', async () => {
+      await defaultKernel.info;
       let called = false;
       defaultKernel.disposed.connect((sender, args) => {
         expect(sender).toBe(defaultKernel);
@@ -74,6 +77,7 @@ describe('Kernel.IKernel', () => {
     });
 
     it('should be emitted when the kernel is shut down', async () => {
+      await defaultKernel.info;
       let called = false;
       defaultKernel.disposed.connect((sender, args) => {
         expect(sender).toBe(defaultKernel);

+ 2 - 0
packages/services/test/kernel/kernel.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { UUID } from '@lumino/coreutils';
 
 import { toArray } from '@lumino/algorithm';

+ 4 - 1
packages/services/test/kernel/manager.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { toArray } from '@lumino/algorithm';
 
 import { KernelManager, Kernel, KernelAPI } from '../../src';
@@ -25,7 +27,7 @@ describe('kernel/manager', () => {
   let kernel: Kernel.IModel;
 
   beforeAll(async () => {
-    jest.setTimeout(120000);
+    jest.setTimeout(20000);
     kernel = await KernelAPI.startNew();
   });
 
@@ -95,6 +97,7 @@ describe('kernel/manager', () => {
           called = true;
         });
         await manager.shutdown(kernel.id);
+        await manager.refreshRunning();
         expect(called).toBe(true);
       });
     });

+ 3 - 1
packages/services/test/session/isession.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { PageConfig } from '@jupyterlab/coreutils';
 
 import { UUID } from '@lumino/coreutils';
@@ -58,7 +60,7 @@ describe('session', () => {
   let defaultSession: Session.ISessionConnection;
 
   beforeAll(async () => {
-    jest.setTimeout(120000);
+    jest.setTimeout(20000);
     defaultSession = await startNew();
   });
 

+ 3 - 1
packages/services/test/session/manager.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { UUID } from '@lumino/coreutils';
 
 import { toArray } from '@lumino/algorithm';
@@ -46,7 +48,7 @@ describe('session/manager', () => {
   let session: Session.ISessionConnection;
 
   beforeAll(() => {
-    jest.setTimeout(120000);
+    jest.setTimeout(20000);
     kernelManager = new KernelManager({ standby: 'never' });
   });
 

+ 2 - 0
packages/services/test/session/session.spec.ts

@@ -2,6 +2,8 @@
 
 import 'jest';
 
+const it = require('jest-retries');
+
 import { UUID } from '@lumino/coreutils';
 
 import { toArray } from '@lumino/algorithm';

+ 0 - 2
tests/test-docregistry/jest.config.js

@@ -1,2 +0,0 @@
-const func = require('@jupyterlab/testutils/lib/jest-config');
-module.exports = func('docregistry', __dirname);

+ 0 - 36
tests/test-docregistry/package.json

@@ -1,36 +0,0 @@
-{
-  "name": "@jupyterlab/test-docregistry",
-  "version": "2.1.0",
-  "private": true,
-  "scripts": {
-    "build": "tsc -b",
-    "clean": "rimraf build && rimraf coverage",
-    "coverage": "python run.py --coverage",
-    "test": "python run.py",
-    "watch": "python run.py --debug",
-    "watch:all": "python run.py --debug --watchAll",
-    "watch:src": "tsc -b --watch"
-  },
-  "dependencies": {
-    "@jupyterlab/apputils": "^2.1.0",
-    "@jupyterlab/docregistry": "^2.1.0",
-    "@jupyterlab/rendermime": "^2.1.0",
-    "@jupyterlab/services": "^5.1.0",
-    "@jupyterlab/testutils": "^2.1.0",
-    "@lumino/algorithm": "^1.2.3",
-    "@lumino/coreutils": "^1.4.2",
-    "@lumino/disposable": "^1.3.5",
-    "@lumino/messaging": "^1.3.3",
-    "@lumino/widgets": "^1.11.1",
-    "chai": "^4.2.0",
-    "jest": "^25.2.3",
-    "jest-junit": "^10.0.0",
-    "ts-jest": "^25.2.1"
-  },
-  "devDependencies": {
-    "@types/chai": "^4.2.7",
-    "@types/jest": "^24.0.23",
-    "rimraf": "~3.0.0",
-    "typescript": "~3.7.3"
-  }
-}

+ 0 - 8
tests/test-docregistry/run.py

@@ -1,8 +0,0 @@
-# Copyright (c) Jupyter Development Team.
-# Distributed under the terms of the Modified BSD License.
-
-import os.path as osp
-from jupyterlab.tests.test_app import run_jest
-
-if __name__ == '__main__':
-    run_jest(osp.dirname(osp.realpath(__file__)))

+ 0 - 27
tests/test-docregistry/tsconfig.json

@@ -1,27 +0,0 @@
-{
-  "extends": "../../tsconfigbase",
-  "compilerOptions": {
-    "outDir": "build",
-    "types": ["jest"],
-    "composite": false,
-    "rootDir": "src"
-  },
-  "include": ["src/*"],
-  "references": [
-    {
-      "path": "../../packages/apputils"
-    },
-    {
-      "path": "../../packages/docregistry"
-    },
-    {
-      "path": "../../packages/rendermime"
-    },
-    {
-      "path": "../../packages/services"
-    },
-    {
-      "path": "../../testutils"
-    }
-  ]
-}

+ 24 - 5
testutils/src/mock.ts

@@ -406,7 +406,8 @@ export const SessionContextMock = jest.fn<
 export const ContentsManagerMock = jest.fn<Contents.IManager, []>(() => {
   const files = new Map<string, Contents.IModel>();
   const dummy = new ContentsManager();
-  const checkpoints: { [key: string]: Contents.ICheckpointModel } = {};
+  const checkpoints = new Map<string, Contents.ICheckpointModel>();
+  const checkPointContent = new Map<string, string>();
 
   const baseModel = Private.createFile({ type: 'directory' });
   files.set('', { ...baseModel, path: '', name: '' });
@@ -426,15 +427,30 @@ export const ContentsManagerMock = jest.fn<Contents.IManager, []>(() => {
     }),
     createCheckpoint: jest.fn(path => {
       const lastModified = new Date().toISOString();
-      checkpoints[path] = { id: UUID.uuid4(), last_modified: lastModified };
-      return Promise.resolve();
+      checkpoints.set(path, { id: UUID.uuid4(), last_modified: lastModified });
+      checkPointContent.set(path, files.get(path)?.content);
+      return Promise.resolve(checkpoints.get(path));
     }),
     listCheckpoints: jest.fn(path => {
-      if (checkpoints[path]) {
-        return Promise.resolve([checkpoints[path]]);
+      if (checkpoints.get(path)) {
+        return Promise.resolve([checkpoints.get(path)]);
       }
       return Promise.resolve([]);
     }),
+    deleteCheckpoint: jest.fn(path => {
+      if (!checkpoints.has(path)) {
+        return Private.makeResponseError(404);
+      }
+      checkpoints.delete(path);
+      return Promise.resolve(void 0);
+    }),
+    restoreCheckpoint: jest.fn(path => {
+      if (!checkpoints.has(path)) {
+        return Private.makeResponseError(404);
+      }
+      (files.get(path) as any).content = checkPointContent.get(path);
+      return Promise.resolve(void 0);
+    }),
     getModelDBFactory: jest.fn(() => {
       return null;
     }),
@@ -503,6 +519,9 @@ export const ContentsManagerMock = jest.fn<Contents.IManager, []>(() => {
       return Promise.resolve(void 0);
     }),
     save: jest.fn((path, options) => {
+      if (path == 'readonly.txt') {
+        return Private.makeResponseError(403);
+      }
       path = Private.fixSlash(path);
       const timeStamp = new Date().toISOString();
       if (files.has(path)) {

+ 5 - 0
yarn.lock

@@ -10630,6 +10630,11 @@ jest-resolve@^25.2.3:
     realpath-native "^2.0.0"
     resolve "^1.15.1"
 
+jest-retries@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/jest-retries/-/jest-retries-1.0.1.tgz#b60eac2c6f6ee7033fbc9a3cb6f3016a63b82822"
+  integrity sha512-tR9tCXs9+Vqw/2toQEOg+CpzOwUqReppcZH2550EnuEhw4F8TR+NbICPUJexegjN9xnuF4ABSGPgzCgAFZI0Ng==
+
 jest-runner@^25.2.3:
   version "25.2.3"
   resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.2.3.tgz#88fb448a46cf4ee9194a3e3cf0adbc122e195adb"