Prechádzať zdrojové kódy

modernize apputils tests

add jest-retry config

Centralize jest-retries config

Centralize flaky config and flakyIt into testutils

Set the type of flakyIt

Fix config handling

Fix import

remove config
Steven Silvester 5 rokov pred
rodič
commit
a8d1f07d54
31 zmenil súbory, kde vykonal 420 pridanie a 386 odobranie
  1. 11 0
      packages/apputils/.vscode/launch.json
  2. 0 0
      packages/apputils/babel.config.js
  3. 2 0
      packages/apputils/jest.config.js
  4. 9 0
      packages/apputils/package.json
  5. 14 15
      packages/apputils/test/commandlinker.spec.ts
  6. 2 0
      packages/apputils/test/dialog.spec.tsx
  7. 16 20
      packages/apputils/test/iframe.spec.ts
  8. 3 1
      packages/apputils/test/inputdialog.spec.ts
  9. 12 13
      packages/apputils/test/mainareawidget.spec.ts
  10. 24 25
      packages/apputils/test/sanitizer.spec.ts
  11. 109 93
      packages/apputils/test/sessioncontext.spec.ts
  12. 14 15
      packages/apputils/test/styling.spec.ts
  13. 75 54
      packages/apputils/test/toolbar.spec.ts
  14. 10 11
      packages/apputils/test/vdom.spec.ts
  15. 47 46
      packages/apputils/test/widgettracker.spec.ts
  16. 39 0
      packages/apputils/tsconfig.test.json
  17. 0 1
      packages/services/package.json
  18. 5 3
      packages/services/test/kernel/comm.spec.ts
  19. 1 3
      packages/services/test/kernel/ifuture.spec.ts
  20. 2 3
      packages/services/test/kernel/ikernel.spec.ts
  21. 2 3
      packages/services/test/kernel/kernel.spec.ts
  22. 6 3
      packages/services/test/kernel/manager.spec.ts
  23. 2 3
      packages/services/test/session/isession.spec.ts
  24. 5 3
      packages/services/test/session/manager.spec.ts
  25. 5 3
      packages/services/test/session/session.spec.ts
  26. 0 2
      tests/test-apputils/jest.config.js
  27. 0 37
      tests/test-apputils/package.json
  28. 0 8
      tests/test-apputils/run.py
  29. 0 21
      tests/test-apputils/tsconfig.json
  30. 1 0
      testutils/package.json
  31. 4 0
      testutils/src/index.ts

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

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

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


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

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

+ 9 - 0
packages/apputils/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": {
@@ -56,9 +61,13 @@
     "sanitize-html": "~1.20.1"
   },
   "devDependencies": {
+    "@jupyterlab/testutils": "^2.1.0",
+    "@types/jest": "^24.0.23",
     "@types/react-dom": "~16.9.4",
     "@types/sanitize-html": "^1.20.2",
+    "jest": "^25.2.3",
     "rimraf": "~3.0.0",
+    "ts-jest": "^25.2.1",
     "typedoc": "^0.15.4",
     "typescript": "~3.7.3"
   },

+ 14 - 15
tests/test-apputils/src/commandlinker.spec.ts → packages/apputils/test/commandlinker.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 { CommandRegistry } from '@lumino/commands';
 
@@ -16,7 +15,7 @@ describe('@jupyterlab/apputils', () => {
     describe('#constructor()', () => {
       it('should create a command linker', () => {
         const linker = new CommandLinker({ commands: new CommandRegistry() });
-        expect(linker).to.be.an.instanceof(CommandLinker);
+        expect(linker).toBeInstanceOf(CommandLinker);
         linker.dispose();
       });
     });
@@ -24,9 +23,9 @@ describe('@jupyterlab/apputils', () => {
     describe('#isDisposed', () => {
       it('should test whether a command linker has been disposed', () => {
         const linker = new CommandLinker({ commands: new CommandRegistry() });
-        expect(linker.isDisposed).to.equal(false);
+        expect(linker.isDisposed).toBe(false);
         linker.dispose();
-        expect(linker.isDisposed).to.equal(true);
+        expect(linker.isDisposed).toBe(true);
       });
     });
 
@@ -46,9 +45,9 @@ describe('@jupyterlab/apputils', () => {
         document.body.appendChild(node);
         linker.connectNode(node, command, undefined);
 
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
         simulate(node, 'click');
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
 
         document.body.removeChild(node);
         linker.dispose();
@@ -73,18 +72,18 @@ describe('@jupyterlab/apputils', () => {
         linker.connectNode(node, command, undefined);
 
         // Make sure connection is working.
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
         simulate(node, 'click');
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
 
         // Reset flag.
         called = false;
 
         // Make sure disconnection is working.
         linker.disconnectNode(node);
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
         simulate(node, 'click');
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
 
         document.body.removeChild(node);
         linker.dispose();
@@ -95,9 +94,9 @@ describe('@jupyterlab/apputils', () => {
     describe('#dispose()', () => {
       it('should dispose the resources held by the linker', () => {
         const linker = new CommandLinker({ commands: new CommandRegistry() });
-        expect(linker.isDisposed).to.equal(false);
+        expect(linker.isDisposed).toBe(false);
         linker.dispose();
-        expect(linker.isDisposed).to.equal(true);
+        expect(linker.isDisposed).toBe(true);
       });
     });
 
@@ -121,9 +120,9 @@ describe('@jupyterlab/apputils', () => {
         node = VirtualDOM.realize(vnode);
         document.body.appendChild(node);
 
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
         simulate(node, 'click');
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
 
         document.body.removeChild(node);
         linker.dispose();

+ 2 - 0
tests/test-apputils/src/dialog.spec.tsx → packages/apputils/test/dialog.spec.tsx

@@ -1,4 +1,6 @@
 // Copyright (c) Jupyter Development Team.
+
+import 'jest';
 // Distributed under the terms of the Modified BSD License.
 
 // import { expect } from 'chai';

+ 16 - 20
tests/test-apputils/src/iframe.spec.ts → packages/apputils/test/iframe.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 { IFrame } from '@jupyterlab/apputils';
 
@@ -10,21 +9,21 @@ describe('@jupyterlab/apputils', () => {
     describe('#constructor()', () => {
       it('should create a new iframe widget', () => {
         const iframe = new IFrame();
-        expect(iframe).to.be.an.instanceof(IFrame);
-        expect(iframe.hasClass('jp-IFrame')).to.equal(true);
-        expect(iframe.node.querySelector('iframe')).to.be.ok;
+        expect(iframe).toBeInstanceOf(IFrame);
+        expect(iframe.hasClass('jp-IFrame')).toBe(true);
+        expect(iframe.node.querySelector('iframe')).toBeTruthy();
       });
 
       it('should be sandboxed by default', () => {
         const iframe = new IFrame();
         const node = iframe.node.querySelector('iframe')!;
-        expect(node.getAttribute('sandbox') !== null).to.equal(true);
+        expect(node.getAttribute('sandbox') !== null).toBe(true);
       });
 
       it('should be have a no-referrer policy by default', () => {
         const iframe = new IFrame();
         const node = iframe.node.querySelector('iframe')!;
-        expect(node.getAttribute('referrerpolicy')).to.equal('no-referrer');
+        expect(node.getAttribute('referrerpolicy')).toBe('no-referrer');
       });
 
       it('should allow sandboxing exceptions to be specified in the options', () => {
@@ -32,7 +31,7 @@ describe('@jupyterlab/apputils', () => {
           sandbox: ['allow-scripts', 'allow-same-origin']
         });
         const node = iframe.node.querySelector('iframe')!;
-        expect(node.getAttribute('sandbox')).to.equal(
+        expect(node.getAttribute('sandbox')).toBe(
           'allow-scripts allow-same-origin'
         );
       });
@@ -40,16 +39,16 @@ describe('@jupyterlab/apputils', () => {
       it('should allow the referrer policy to be specified in the options', () => {
         const iframe = new IFrame({ referrerPolicy: 'unsafe-url' });
         const node = iframe.node.querySelector('iframe')!;
-        expect(node.getAttribute('referrerpolicy')).to.equal('unsafe-url');
+        expect(node.getAttribute('referrerpolicy')).toBe('unsafe-url');
       });
     });
 
     describe('#url', () => {
       it('should be the url of the iframe', () => {
         const iframe = new IFrame();
-        expect(iframe.url).to.equal('');
+        expect(iframe.url).toBe('');
         iframe.url = 'foo';
-        expect(iframe.url).to.equal('foo');
+        expect(iframe.url).toBe('foo');
       });
     });
 
@@ -57,10 +56,10 @@ describe('@jupyterlab/apputils', () => {
       it('should set the referrer policy for the iframe.', () => {
         const iframe = new IFrame({ referrerPolicy: 'unsafe-url' });
         const node = iframe.node.querySelector('iframe')!;
-        expect(iframe.referrerPolicy).to.equal('unsafe-url');
+        expect(iframe.referrerPolicy).toBe('unsafe-url');
         iframe.referrerPolicy = 'origin';
-        expect(iframe.referrerPolicy).to.equal('origin');
-        expect(node.getAttribute('referrerpolicy')).to.equal('origin');
+        expect(iframe.referrerPolicy).toBe('origin');
+        expect(node.getAttribute('referrerpolicy')).toBe('origin');
       });
     });
 
@@ -70,13 +69,10 @@ describe('@jupyterlab/apputils', () => {
           sandbox: ['allow-scripts', 'allow-same-origin']
         });
         const node = iframe.node.querySelector('iframe')!;
-        expect(iframe.sandbox).to.deep.equal([
-          'allow-scripts',
-          'allow-same-origin'
-        ]);
+        expect(iframe.sandbox).toEqual(['allow-scripts', 'allow-same-origin']);
         iframe.sandbox = ['allow-pointer-lock'];
-        expect(iframe.sandbox).to.deep.equal(['allow-pointer-lock']);
-        expect(node.getAttribute('sandbox')).to.equal('allow-pointer-lock');
+        expect(iframe.sandbox).toEqual(['allow-pointer-lock']);
+        expect(node.getAttribute('sandbox')).toBe('allow-pointer-lock');
       });
     });
   });

+ 3 - 1
tests/test-apputils/src/inputdialog.spec.ts → packages/apputils/test/inputdialog.spec.ts

@@ -1,4 +1,6 @@
 // Copyright (c) Jupyter Development Team.
+
+import 'jest';
 // Distributed under the terms of the Modified BSD License.
 
 import { InputDialog } from '@jupyterlab/apputils';
@@ -293,7 +295,7 @@ describe('@jupyterlab/apputils', () => {
         const result = await prompt;
 
         expect(result.button.accept).toBe(true);
-        expect(result.value).toBe(Number.NaN);
+        expect(result.value).toBeNaN();
         document.body.removeChild(node);
       });
     });

+ 12 - 13
tests/test-apputils/src/mainareawidget.spec.ts → packages/apputils/test/mainareawidget.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 { MainAreaWidget, Toolbar } from '@jupyterlab/apputils';
 
@@ -15,17 +14,17 @@ describe('@jupyterlab/apputils', () => {
       it('should create a new main area widget', () => {
         const content = new Widget();
         const widget = new MainAreaWidget({ content });
-        expect(widget).to.be.an.instanceof(MainAreaWidget);
-        expect(widget.hasClass('jp-MainAreaWidget')).to.equal(true);
-        expect(widget.content.node.tabIndex).to.equal(-1);
-        expect(widget.title.closable).to.equal(true);
+        expect(widget).toBeInstanceOf(MainAreaWidget);
+        expect(widget.hasClass('jp-MainAreaWidget')).toBe(true);
+        expect(widget.content.node.tabIndex).toBe(-1);
+        expect(widget.title.closable).toBe(true);
       });
 
       it('should allow toolbar options', () => {
         const content = new Widget();
         const toolbar = new Toolbar();
         const widget = new MainAreaWidget({ content, toolbar });
-        expect(widget.hasClass('jp-MainAreaWidget')).to.equal(true);
+        expect(widget.hasClass('jp-MainAreaWidget')).toBe(true);
       });
     });
 
@@ -35,7 +34,7 @@ describe('@jupyterlab/apputils', () => {
         const widget = new MainAreaWidget({ content });
         Widget.attach(widget, document.body);
         MessageLoop.sendMessage(widget, Widget.Msg.ActivateRequest);
-        expect(document.activeElement).to.equal(widget.content.node);
+        expect(document.activeElement).toBe(widget.content.node);
       });
     });
 
@@ -45,7 +44,7 @@ describe('@jupyterlab/apputils', () => {
         const widget = new MainAreaWidget({ content });
         Widget.attach(widget, document.body);
         MessageLoop.sendMessage(widget, Widget.Msg.CloseRequest);
-        expect(widget.isDisposed).to.equal(true);
+        expect(widget.isDisposed).toBe(true);
       });
     });
 
@@ -61,7 +60,7 @@ describe('@jupyterlab/apputils', () => {
         Widget.attach(widget, document.body);
         updated = false;
         MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
-        expect(updated).to.equal(true);
+        expect(updated).toBe(true);
       });
     });
 
@@ -70,14 +69,14 @@ describe('@jupyterlab/apputils', () => {
         const content = new Widget();
         const widget = new MainAreaWidget({ content });
         content.title.label = 'foo';
-        expect(widget.title.label).to.equal('foo');
+        expect(widget.title.label).toBe('foo');
       });
 
       it('should proxy from main to content', () => {
         const content = new Widget();
         const widget = new MainAreaWidget({ content });
         widget.title.label = 'foo';
-        expect(content.title.label).to.equal('foo');
+        expect(content.title.label).toBe('foo');
       });
     });
 
@@ -86,7 +85,7 @@ describe('@jupyterlab/apputils', () => {
         const content = new Widget();
         const widget = new MainAreaWidget({ content });
         content.dispose();
-        expect(widget.isDisposed).to.equal(true);
+        expect(widget.isDisposed).toBe(true);
       });
     });
   });

+ 24 - 25
tests/test-apputils/src/sanitizer.spec.ts → packages/apputils/test/sanitizer.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 { defaultSanitizer } from '@jupyterlab/apputils';
 
@@ -9,87 +8,87 @@ describe('defaultSanitizer', () => {
   describe('#sanitize()', () => {
     it('should allow h1 tags', () => {
       const h1 = '<h1>foo</h1>';
-      expect(defaultSanitizer.sanitize(h1)).to.equal(h1);
+      expect(defaultSanitizer.sanitize(h1)).toBe(h1);
     });
 
     it('should allow h2 tags', () => {
       const h2 = '<h2>foo</h2>';
-      expect(defaultSanitizer.sanitize(h2)).to.equal(h2);
+      expect(defaultSanitizer.sanitize(h2)).toBe(h2);
     });
 
     it('should not allow svg tags', () => {
       const svg = '<svg>foo</svg>';
-      expect(defaultSanitizer.sanitize(svg)).to.equal('foo');
+      expect(defaultSanitizer.sanitize(svg)).toBe('foo');
     });
 
     it('should allow img tags and some attributes', () => {
       const img =
         '<img src="smiley.gif" alt="Smiley face" height="42" width="42" />';
-      expect(defaultSanitizer.sanitize(img)).to.equal(img);
+      expect(defaultSanitizer.sanitize(img)).toBe(img);
     });
 
     it('should allow span tags and class attribute', () => {
       const span = '<span class="foo">bar</span>';
-      expect(defaultSanitizer.sanitize(span)).to.equal(span);
+      expect(defaultSanitizer.sanitize(span)).toBe(span);
     });
 
     it('should set the rel attribute for <a> tags to "nofollow', () => {
       const a = '<a rel="foo" href="bar">Baz</a>';
       const expected = a.replace('foo', 'nofollow');
-      expect(defaultSanitizer.sanitize(a)).to.equal(expected);
+      expect(defaultSanitizer.sanitize(a)).toBe(expected);
     });
 
     it('should allow the class attribute for code tags', () => {
       const code = '<code class="foo">bar</code>';
-      expect(defaultSanitizer.sanitize(code)).to.equal(code);
+      expect(defaultSanitizer.sanitize(code)).toBe(code);
     });
 
     it('should allow the class attribute for div tags', () => {
       const div = '<div class="foo">bar</div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal(div);
+      expect(defaultSanitizer.sanitize(div)).toBe(div);
     });
 
     it('should allow the class attribute for p tags', () => {
       const p = '<p class="foo">bar</p>';
-      expect(defaultSanitizer.sanitize(p)).to.equal(p);
+      expect(defaultSanitizer.sanitize(p)).toBe(p);
     });
 
     it('should allow the class attribute for pre tags', () => {
       const pre = '<pre class="foo">bar</pre>';
-      expect(defaultSanitizer.sanitize(pre)).to.equal(pre);
+      expect(defaultSanitizer.sanitize(pre)).toBe(pre);
     });
 
     it('should strip script tags', () => {
       const script = '<script>alert("foo")</script>';
-      expect(defaultSanitizer.sanitize(script)).to.equal('');
+      expect(defaultSanitizer.sanitize(script)).toBe('');
     });
 
     it('should strip iframe tags', () => {
       const script = '<iframe src=""></iframe>';
-      expect(defaultSanitizer.sanitize(script)).to.equal('');
+      expect(defaultSanitizer.sanitize(script)).toBe('');
     });
 
     it('should strip link tags', () => {
       const link = '<link rel="stylesheet" type="text/css" href="theme.css">';
-      expect(defaultSanitizer.sanitize(link)).to.equal('');
+      expect(defaultSanitizer.sanitize(link)).toBe('');
     });
 
     it('should pass through simple well-formed whitelisted markup', () => {
       const div = '<div><p>Hello <b>there</b></p></div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal(div);
+      expect(defaultSanitizer.sanitize(div)).toBe(div);
     });
 
     it('should allow video tags with some attributes', () => {
       const video =
         '<video src="my/video.mp4" height="42" width="42"' +
         ' autoplay controls loop muted></video>';
-      expect(defaultSanitizer.sanitize(video)).to.equal(video);
+      expect(defaultSanitizer.sanitize(video)).toBe(video);
     });
 
     it('should allow audio tags with some attributes', () => {
       const audio =
         '<audio src="my/audio.ogg autoplay loop ' + 'controls muted"></audio>';
-      expect(defaultSanitizer.sanitize(audio)).to.equal(audio);
+      expect(defaultSanitizer.sanitize(audio)).toBe(audio);
     });
 
     it('should allow input tags but disable them', () => {
@@ -102,41 +101,41 @@ describe('defaultSanitizer', () => {
       div.innerHTML = html;
       input = div.querySelector('input')!;
 
-      expect(input.disabled).to.equal(true);
+      expect(input.disabled).toBe(true);
     });
 
     // Test unwanted inline CSS style stripping
 
     it('should allow harmless inline CSS', () => {
       const div = '<div style="color:green"></div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal(div);
+      expect(defaultSanitizer.sanitize(div)).toBe(div);
     });
 
     it("should strip 'content' properties from inline CSS", () => {
       const div = '<div style="color: green; content: attr(title)"></div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal(
+      expect(defaultSanitizer.sanitize(div)).toBe(
         '<div style="color:green"></div>'
       );
     });
 
     it("should strip 'counter-increment' properties from inline CSS", () => {
       const div = '<div style="counter-increment: example-counter;"></div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal('<div></div>');
+      expect(defaultSanitizer.sanitize(div)).toBe('<div></div>');
     });
 
     it("should strip 'counter-reset' properties from inline CSS", () => {
       const div = '<div style="counter-reset: chapter-count 0;"></div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal('<div></div>');
+      expect(defaultSanitizer.sanitize(div)).toBe('<div></div>');
     });
 
     it("should strip 'widows' properties from inline CSS", () => {
       const div = '<div style="widows: 2;"></div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal('<div></div>');
+      expect(defaultSanitizer.sanitize(div)).toBe('<div></div>');
     });
 
     it("should strip 'orphans' properties from inline CSS", () => {
       const div = '<div style="orphans: 3;"></div>';
-      expect(defaultSanitizer.sanitize(div)).to.equal('<div></div>');
+      expect(defaultSanitizer.sanitize(div)).toBe('<div></div>');
     });
   });
 });

+ 109 - 93
tests/test-apputils/src/sessioncontext.spec.ts → packages/apputils/test/sessioncontext.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 {
   SessionManager,
@@ -16,32 +15,47 @@ import {
   sessionContextDialogs
 } from '@jupyterlab/apputils';
 
-import { UUID } from '@lumino/coreutils';
+import { UUID, PromiseDelegate } from '@lumino/coreutils';
 
 import {
   acceptDialog,
   dismissDialog,
-  testEmission
+  testEmission,
+  JupyterServer,
+  flakyIt as it
 } from '@jupyterlab/testutils';
 
 import { SessionAPI } from '@jupyterlab/services';
 
+const server = new JupyterServer();
+
+beforeAll(async () => {
+  await server.start();
+});
+
+afterAll(async () => {
+  await server.shutdown();
+});
+
 describe('@jupyterlab/apputils', () => {
   describe('SessionContext', () => {
-    const kernelManager = new KernelManager();
-    const sessionManager = new SessionManager({ kernelManager });
-    const specsManager = new KernelSpecManager();
+    let kernelManager: KernelManager;
+    let sessionManager: SessionManager;
+    let specsManager: KernelSpecManager;
     let path = '';
     let sessionContext: SessionContext;
 
-    beforeAll(
-      async () =>
-        await Promise.all([
-          sessionManager.ready,
-          kernelManager.ready,
-          specsManager.ready
-        ])
-    );
+    beforeAll(async () => {
+      jest.setTimeout(20000);
+      kernelManager = new KernelManager();
+      sessionManager = new SessionManager({ kernelManager });
+      specsManager = new KernelSpecManager();
+      await Promise.all([
+        sessionManager.ready,
+        kernelManager.ready,
+        specsManager.ready
+      ]);
+    });
 
     beforeEach(async () => {
       Dialog.flush();
@@ -66,7 +80,7 @@ describe('@jupyterlab/apputils', () => {
 
     describe('#constructor()', () => {
       it('should create a session context', () => {
-        expect(sessionContext).to.be.an.instanceof(SessionContext);
+        expect(sessionContext).toBeInstanceOf(SessionContext);
       });
     });
 
@@ -76,12 +90,12 @@ describe('@jupyterlab/apputils', () => {
         await sessionContext.initialize();
         let called = false;
         sessionContext.disposed.connect((sender, args) => {
-          expect(sender).to.equal(sessionContext);
-          expect(args).to.be.undefined;
+          expect(sender).toBe(sessionContext);
+          expect(args).toBeUndefined();
           called = true;
         });
         sessionContext.dispose();
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
     });
 
@@ -90,14 +104,14 @@ describe('@jupyterlab/apputils', () => {
         let called = false;
         sessionContext.kernelChanged.connect(
           (sender, { oldValue, newValue }) => {
-            expect(sender).to.equal(sessionContext);
-            expect(oldValue).to.be.null;
-            expect(newValue).to.equal(sessionContext.session?.kernel);
+            expect(sender).toBe(sessionContext);
+            expect(oldValue).toBeNull();
+            expect(newValue).toBe(sessionContext.session?.kernel);
             called = true;
           }
         );
         await sessionContext.initialize();
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
     });
 
@@ -106,14 +120,14 @@ describe('@jupyterlab/apputils', () => {
         let called = false;
         sessionContext.sessionChanged.connect(
           (sender, { oldValue, newValue }) => {
-            expect(sender).to.equal(sessionContext);
-            expect(oldValue).to.be.null;
-            expect(newValue).to.equal(sessionContext.session);
+            expect(sender).toBe(sessionContext);
+            expect(oldValue).toBeNull();
+            expect(newValue).toBe(sessionContext.session);
             called = true;
           }
         );
         await sessionContext.initialize();
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
     });
 
@@ -121,13 +135,13 @@ describe('@jupyterlab/apputils', () => {
       it('should be emitted when the status changes', async () => {
         let called = false;
         sessionContext.statusChanged.connect((sender, args) => {
-          expect(sender).to.equal(sessionContext);
-          expect(typeof args).to.equal('string');
+          expect(sender).toBe(sessionContext);
+          expect(typeof args).toBe('string');
           called = true;
         });
         await sessionContext.initialize();
         await sessionContext.session!.kernel!.info;
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
     });
 
@@ -135,12 +149,12 @@ describe('@jupyterlab/apputils', () => {
       it('should be emitted for iopub kernel messages', async () => {
         let called = false;
         sessionContext.iopubMessage.connect((sender, args) => {
-          expect(sender).to.equal(sessionContext);
+          expect(sender).toBe(sessionContext);
           called = true;
         });
         await sessionContext.initialize();
         await sessionContext.session!.kernel!.info;
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
     });
 
@@ -149,24 +163,24 @@ describe('@jupyterlab/apputils', () => {
         let called = false;
         await sessionContext.initialize();
         sessionContext.propertyChanged.connect((sender, args) => {
-          expect(sender).to.equal(sessionContext);
-          expect(args).to.equal('path');
+          expect(sender).toBe(sessionContext);
+          expect(args).toBe('path');
           called = true;
         });
         await sessionContext.session!.setPath('foo');
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
 
       it('should be emitted when a session name changes', async () => {
         let called = false;
         await sessionContext.initialize();
         sessionContext.propertyChanged.connect((sender, args) => {
-          expect(sender).to.equal(sessionContext);
-          expect(args).to.equal('name');
+          expect(sender).toBe(sessionContext);
+          expect(args).toBe('name');
           called = true;
         });
         await sessionContext.session!.setName('foo');
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
 
       it('should be emitted when a session type changes', async () => {
@@ -174,20 +188,20 @@ describe('@jupyterlab/apputils', () => {
 
         await sessionContext.initialize();
         sessionContext.propertyChanged.connect((sender, args) => {
-          expect(sender).to.equal(sessionContext);
-          expect(args).to.equal('type');
+          expect(sender).toBe(sessionContext);
+          expect(args).toBe('type');
           called = true;
         });
         await sessionContext.session!.setType('foo');
-        expect(called).to.be.true;
+        expect(called).toBe(true);
       });
     });
 
     describe('#kernel', () => {
       it('should be the current kernel of the the session', async () => {
-        expect(sessionContext.session?.kernel).to.not.be.ok;
+        expect(sessionContext.session?.kernel).toBeFalsy();
         await sessionContext.initialize();
-        expect(sessionContext.session?.kernel).to.be.ok;
+        expect(sessionContext.session?.kernel).toBeTruthy();
       });
     });
 
@@ -201,20 +215,20 @@ describe('@jupyterlab/apputils', () => {
           canStart: true
         };
         sessionContext.kernelPreference = preference;
-        expect(sessionContext.kernelPreference).to.equal(preference);
+        expect(sessionContext.kernelPreference).toBe(preference);
       });
     });
 
     describe('#manager', () => {
       it('should be the session manager used by the session', () => {
-        expect(sessionContext.sessionManager).to.equal(sessionManager);
+        expect(sessionContext.sessionManager).toBe(sessionManager);
       });
     });
 
     describe('#initialize()', () => {
       it('should start the default kernel', async () => {
         await sessionContext.initialize();
-        expect(sessionContext.session?.kernel?.name).to.equal(
+        expect(sessionContext.session?.kernel?.name).toBe(
           specsManager.specs!.default
         );
       });
@@ -227,8 +241,8 @@ describe('@jupyterlab/apputils', () => {
         });
 
         await sessionContext.initialize();
-        expect(other.kernel?.id).to.not.be.undefined;
-        expect(other.kernel?.id).to.equal(sessionContext.session?.kernel?.id);
+        expect(other.kernel?.id).toBeDefined();
+        expect(other.kernel?.id).toBe(sessionContext.session?.kernel?.id);
         await other.shutdown();
         other.dispose();
       });
@@ -250,8 +264,8 @@ describe('@jupyterlab/apputils', () => {
           kernelPreference
         });
         await sessionContext.initialize();
-        expect(other.kernel?.id).to.not.be.undefined;
-        expect(other.kernel?.id).to.equal(sessionContext.session?.kernel?.id);
+        expect(other.kernel?.id).toBeDefined();
+        expect(other.kernel?.id).toBe(sessionContext.session?.kernel?.id);
         // We don't call other.shutdown() here because that
         // is handled by the afterEach() handler above.
         other.dispose();
@@ -261,21 +275,21 @@ describe('@jupyterlab/apputils', () => {
         // Remove the kernel preference before initializing.
         sessionContext.kernelPreference = {};
         const result = await sessionContext.initialize();
-        expect(result).to.equal(true);
+        expect(result).toBe(true);
       });
 
       it('should be a no-op if the shouldStart kernelPreference is false', async () => {
         sessionContext.kernelPreference = { shouldStart: false };
         const result = await sessionContext.initialize();
-        expect(result).to.equal(false);
-        expect(sessionContext.session?.kernel).to.not.be.ok;
+        expect(result).toBe(false);
+        expect(sessionContext.session?.kernel).toBeFalsy();
       });
 
       it('should be a no-op if the canStart kernelPreference is false', async () => {
         sessionContext.kernelPreference = { canStart: false };
         const result = await sessionContext.initialize();
-        expect(result).to.equal(false);
-        expect(sessionContext.session?.kernel).to.not.be.ok;
+        expect(result).toBe(false);
+        expect(sessionContext.session?.kernel).toBeFalsy();
       });
     });
 
@@ -283,7 +297,7 @@ describe('@jupyterlab/apputils', () => {
       it('should be the display name of the current kernel', async () => {
         await sessionContext.initialize();
         const spec = await sessionContext.session!.kernel!.spec;
-        expect(sessionContext.kernelDisplayName).to.equal(spec!.display_name);
+        expect(sessionContext.kernelDisplayName).toBe(spec!.display_name);
       });
 
       it('should display "No Kernel" when there is no kernel', async () => {
@@ -291,7 +305,7 @@ describe('@jupyterlab/apputils', () => {
           canStart: false,
           shouldStart: false
         };
-        expect(sessionContext.kernelDisplayName).to.equal('No Kernel');
+        expect(sessionContext.kernelDisplayName).toBe('No Kernel');
       });
 
       it('should display the pending kernel name when it looks like we are starting a kernel', async () => {
@@ -300,7 +314,7 @@ describe('@jupyterlab/apputils', () => {
           canStart: true,
           shouldStart: true
         };
-        expect(sessionContext.kernelDisplayName).to.equal('Echo Kernel');
+        expect(sessionContext.kernelDisplayName).toBe('Echo Kernel');
       });
     });
 
@@ -308,7 +322,7 @@ describe('@jupyterlab/apputils', () => {
       it('should be the status of the current kernel if connected', async () => {
         await sessionContext.initialize();
         await sessionContext.session!.kernel!.info;
-        expect(sessionContext.kernelDisplayStatus).to.be.equal(
+        expect(sessionContext.kernelDisplayStatus).toBe(
           sessionContext.session?.kernel?.status
         );
       });
@@ -316,7 +330,7 @@ describe('@jupyterlab/apputils', () => {
       it('should be the connection status of the current kernel if not connected', async () => {
         await sessionContext.initialize();
         const reconnect = sessionContext.session!.kernel!.reconnect();
-        expect(sessionContext.kernelDisplayStatus).to.be.equal(
+        expect(sessionContext.kernelDisplayStatus).toBe(
           sessionContext.session?.kernel?.connectionStatus
         );
         await reconnect;
@@ -324,30 +338,30 @@ describe('@jupyterlab/apputils', () => {
 
       it('should be "initializing" if it looks like we are trying to start a kernel', async () => {
         sessionContext.kernelPreference = {};
-        expect(sessionContext.kernelDisplayStatus).to.be.equal('initializing');
+        expect(sessionContext.kernelDisplayStatus).toBe('initializing');
       });
 
       it('should be "idle" if there is no current kernel', async () => {
         await sessionContext.initialize();
         await sessionContext.shutdown();
-        expect(sessionContext.kernelDisplayStatus).to.be.equal('idle');
+        expect(sessionContext.kernelDisplayStatus).toBe('idle');
       });
     });
 
     describe('#isDisposed', () => {
       it('should test whether a client session has been disposed', () => {
-        expect(sessionContext.isDisposed).to.equal(false);
+        expect(sessionContext.isDisposed).toBe(false);
         sessionContext.dispose();
-        expect(sessionContext.isDisposed).to.equal(true);
+        expect(sessionContext.isDisposed).toBe(true);
       });
     });
 
     describe('#dispose()', () => {
       it('should dispose the resources held by the client session', () => {
         sessionContext.dispose();
-        expect(sessionContext.isDisposed).to.equal(true);
+        expect(sessionContext.isDisposed).toBe(true);
         sessionContext.dispose();
-        expect(sessionContext.isDisposed).to.equal(true);
+        expect(sessionContext.isDisposed).toBe(true);
       });
 
       it('should not shut down the session by default', async () => {
@@ -355,25 +369,27 @@ describe('@jupyterlab/apputils', () => {
         const id = sessionContext.session!.id;
         sessionContext.dispose();
         const sessions = await SessionAPI.listRunning();
-        expect(sessions.find(s => s.id === id)).to.be.ok;
+        expect(sessions.find(s => s.id === id)).toBeTruthy();
         await SessionAPI.shutdownSession(id);
       });
 
-      it('should shut down the session when shutdownOnDispose is true', async done => {
+      it('should shut down the session when shutdownOnDispose is true', async () => {
         sessionContext.kernelPreference = {
           ...sessionContext.kernelPreference,
           shutdownOnDispose: true
         };
+        const delegate = new PromiseDelegate();
         await sessionContext.initialize();
         const id = sessionContext.session!.id;
         // Wait for the session to shut down.
         sessionContext.sessionManager.runningChanged.connect((_, sessions) => {
           if (!sessions.find(s => s.id === id)) {
-            done();
+            delegate.resolve(void 0);
             return;
           }
         });
         sessionContext.dispose();
+        return delegate.promise;
       });
     });
 
@@ -385,8 +401,8 @@ describe('@jupyterlab/apputils', () => {
         const id = sessionContext.session?.kernel?.id;
         const kernel = (await sessionContext.changeKernel({ name }))!;
 
-        expect(kernel.id).to.not.equal(id);
-        expect(kernel.name).to.equal(name);
+        expect(kernel.id).not.toBe(id);
+        expect(kernel.name).toBe(name);
       });
 
       it('should still work if called before fully initialized', async () => {
@@ -401,8 +417,8 @@ describe('@jupyterlab/apputils', () => {
         const results = await Promise.all([kernelPromise, initPromise]);
         const kernel = results[0];
         const shouldSelect = results[1];
-        expect(shouldSelect).to.equal(false);
-        expect(lastKernel).to.equal(kernel);
+        expect(shouldSelect).toBe(false);
+        expect(lastKernel).toBe(kernel);
       });
 
       it('should handle multiple requests', async () => {
@@ -419,7 +435,7 @@ describe('@jupyterlab/apputils', () => {
         const results = await Promise.all([kernelPromise0, kernelPromise1]);
         // We can't know which of the two was launched first, so the result
         // could be either, just make sure it isn't the original kernel.
-        expect(lastKernel).to.be.oneOf([results[0], results[1]]);
+        expect([results[0], results[1]]).toContain(lastKernel);
       });
 
       it('should handle an error during kernel change', async () => {
@@ -436,25 +452,25 @@ describe('@jupyterlab/apputils', () => {
             caught = true;
           });
         await Promise.all([promise, acceptDialog()]);
-        expect(caught).to.equal(true);
-        expect(status).to.equal('unknown');
+        expect(caught).toBe(true);
+        expect(status).toBe('unknown');
       });
     });
 
     describe('#shutdown', () => {
       it('should kill the kernel and shut down the session', async () => {
         await sessionContext.initialize();
-        expect(sessionContext.session?.kernel).to.be.ok;
+        expect(sessionContext.session?.kernel).toBeTruthy();
         await sessionContext.shutdown();
-        expect(sessionContext.session?.kernel).to.not.be.ok;
+        expect(sessionContext.session?.kernel).toBeFalsy();
       });
 
       it('should handle a shutdown during startup', async () => {
         const initPromise = sessionContext.initialize(); // Start but don't finish init.
         const shutdownPromise = sessionContext.shutdown();
         const results = await Promise.all([initPromise, shutdownPromise]);
-        expect(results[0]).to.equal(false);
-        expect(sessionContext.session).to.equal(null);
+        expect(results[0]).toBe(false);
+        expect(sessionContext.session).toBe(null);
       });
     });
 
@@ -465,7 +481,7 @@ describe('@jupyterlab/apputils', () => {
             specs: specsManager.specs,
             preference: {}
           })
-        ).to.be.null;
+        ).toBeNull();
       });
 
       it('should return a matching name', () => {
@@ -478,7 +494,7 @@ describe('@jupyterlab/apputils', () => {
             specs: specsManager.specs,
             preference: { name: spec.name }
           })
-        ).to.equal(spec.name);
+        ).toBe(spec.name);
       });
 
       it('should return null if no match is found', () => {
@@ -487,7 +503,7 @@ describe('@jupyterlab/apputils', () => {
             specs: specsManager.specs,
             preference: { name: 'foo' }
           })
-        ).to.be.null;
+        ).toBeNull();
       });
 
       it('should return a matching language', () => {
@@ -505,7 +521,7 @@ describe('@jupyterlab/apputils', () => {
             },
             preference: { language: spec.language }
           })
-        ).to.equal(spec.name);
+        ).toBe(spec.name);
       });
 
       it('should return null if a language matches twice', () => {
@@ -524,7 +540,7 @@ describe('@jupyterlab/apputils', () => {
             },
             preference: { language: spec.language }
           })
-        ).to.be.null;
+        ).toBeNull();
       });
     });
 
@@ -540,8 +556,8 @@ describe('@jupyterlab/apputils', () => {
           await accept;
 
           const session = sessionContext?.session;
-          expect(session!.kernel!.id).to.not.equal(id);
-          expect(session!.kernel!.name).to.equal(name);
+          expect(session!.kernel!.id).not.toBe(id);
+          expect(session!.kernel!.name).toBe(name);
         });
 
         it('should keep the existing kernel if dismissed', async () => {
@@ -554,8 +570,8 @@ describe('@jupyterlab/apputils', () => {
           await dismiss;
 
           const session = sessionContext.session;
-          expect(session!.kernel!.id).to.equal(id);
-          expect(session!.kernel!.name).to.equal(name);
+          expect(session!.kernel!.id).toBe(id);
+          expect(session!.kernel!.name).toBe(name);
         });
       });
 
@@ -569,7 +585,7 @@ describe('@jupyterlab/apputils', () => {
           const restart = sessionContextDialogs.restart(sessionContext);
 
           await acceptDialog();
-          expect(await restart).to.equal(true);
+          expect(await restart).toBe(true);
           await emission;
         });
 
@@ -585,15 +601,15 @@ describe('@jupyterlab/apputils', () => {
 
           const restart = sessionContextDialogs.restart(sessionContext);
           await dismissDialog();
-          expect(await restart).to.equal(false);
-          expect(called).to.equal(false);
+          expect(await restart).toBe(false);
+          expect(called).toBe(false);
         });
 
         it('should start the same kernel as the previously started kernel', async () => {
           await sessionContext.initialize();
           await sessionContext.shutdown();
           await sessionContextDialogs.restart(sessionContext);
-          expect(sessionContext?.session?.kernel).to.be.ok;
+          expect(sessionContext?.session?.kernel).toBeTruthy();
         });
       });
     });

+ 14 - 15
tests/test-apputils/src/styling.spec.ts → packages/apputils/test/styling.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 { VirtualDOM, h } from '@lumino/virtualdom';
 
@@ -16,7 +15,7 @@ describe('@jupyterlab/apputils', () => {
         const vnode = h.div({}, [h.button(), h.select(), h.input()]);
         const node = VirtualDOM.realize(vnode);
         Styling.styleNode(node);
-        expect(node.querySelectorAll('.jp-mod-styled').length).to.equal(3);
+        expect(node.querySelectorAll('.jp-mod-styled').length).toBe(3);
       });
 
       it('should wrap a select node', () => {
@@ -25,16 +24,16 @@ describe('@jupyterlab/apputils', () => {
         parent.appendChild(select);
         Styling.styleNode(parent);
         const wrapper = parent.firstChild as HTMLElement;
-        expect(wrapper.className).to.equal('jp-select-wrapper');
-        expect(select.parentElement).to.equal(wrapper);
-        expect(select.className).to.equal('jp-mod-styled');
+        expect(wrapper.className).toBe('jp-select-wrapper');
+        expect(select.parentElement).toBe(wrapper);
+        expect(select.className).toBe('jp-mod-styled');
         document.body.appendChild(parent);
         select.focus();
         simulate(select, 'focus');
-        expect(wrapper.className).to.contain('jp-mod-focused');
+        expect(wrapper.className).toContain('jp-mod-focused');
         select.blur();
         simulate(select, 'blur');
-        expect(wrapper.className).to.not.contain('jp-mod-focused');
+        expect(wrapper.className).not.toContain('jp-mod-focused');
         document.body.removeChild(parent);
       });
     });
@@ -44,13 +43,13 @@ describe('@jupyterlab/apputils', () => {
         const vnode = h.div({}, [h.span(), h.div({}, h.span())]);
         const node = VirtualDOM.realize(vnode);
         Styling.styleNodeByTag(node, 'span');
-        expect(node.querySelectorAll('.jp-mod-styled').length).to.equal(2);
+        expect(node.querySelectorAll('.jp-mod-styled').length).toBe(2);
       });
 
       it('should style the node itself', () => {
         const div = document.createElement('div');
         Styling.styleNodeByTag(div, 'div');
-        expect(div.className).to.contain('jp-mod-styled');
+        expect(div.className).toContain('jp-mod-styled');
       });
     });
 
@@ -58,16 +57,16 @@ describe('@jupyterlab/apputils', () => {
       it('should wrap the select node', () => {
         const select = document.createElement('select');
         const wrapper = Styling.wrapSelect(select);
-        expect(wrapper.className).to.equal('jp-select-wrapper');
-        expect(select.parentElement).to.equal(wrapper);
-        expect(select.className).to.equal('jp-mod-styled');
+        expect(wrapper.className).toBe('jp-select-wrapper');
+        expect(select.parentElement).toBe(wrapper);
+        expect(select.className).toBe('jp-mod-styled');
         document.body.appendChild(wrapper);
         select.focus();
         simulate(select, 'focus');
-        expect(wrapper.className).to.contain('jp-mod-focused');
+        expect(wrapper.className).toContain('jp-mod-focused');
         select.blur();
         simulate(select, 'blur');
-        expect(wrapper.className).to.not.contain('jp-mod-focused');
+        expect(wrapper.className).not.toContain('jp-mod-focused');
         document.body.removeChild(wrapper);
       });
     });

+ 75 - 54
tests/test-apputils/src/toolbar.spec.ts → packages/apputils/test/toolbar.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 {
   Toolbar,
@@ -20,12 +19,27 @@ import { Widget } from '@lumino/widgets';
 
 import { simulate } from 'simulate-event';
 
-import { createSessionContext, framePromise } from '@jupyterlab/testutils';
+import {
+  createSessionContext,
+  framePromise,
+  JupyterServer
+} from '@jupyterlab/testutils';
+
+const server = new JupyterServer();
+
+beforeAll(async () => {
+  await server.start();
+});
+
+afterAll(async () => {
+  await server.shutdown();
+});
 
 describe('@jupyterlab/apputils', () => {
   let widget: Toolbar<Widget>;
 
   beforeEach(async () => {
+    jest.setTimeout(20000);
     widget = new Toolbar();
   });
 
@@ -37,12 +51,12 @@ describe('@jupyterlab/apputils', () => {
     describe('#constructor()', () => {
       it('should construct a new toolbar widget', () => {
         const widget = new Toolbar();
-        expect(widget).to.be.an.instanceof(Toolbar);
+        expect(widget).toBeInstanceOf(Toolbar);
       });
 
       it('should add the `jp-Toolbar` class', () => {
         const widget = new Toolbar();
-        expect(widget.hasClass('jp-Toolbar')).to.equal(true);
+        expect(widget.hasClass('jp-Toolbar')).toBe(true);
       });
     });
 
@@ -51,26 +65,26 @@ describe('@jupyterlab/apputils', () => {
         widget.addItem('foo', new Widget());
         widget.addItem('bar', new Widget());
         widget.addItem('baz', new Widget());
-        expect(toArray(widget.names())).to.deep.equal(['foo', 'bar', 'baz']);
+        expect(toArray(widget.names())).toEqual(['foo', 'bar', 'baz']);
       });
     });
 
     describe('#addItem()', () => {
       it('should add an item to the toolbar', () => {
         const item = new Widget();
-        expect(widget.addItem('test', item)).to.equal(true);
-        expect(toArray(widget.names())).to.contain('test');
+        expect(widget.addItem('test', item)).toBe(true);
+        expect(toArray(widget.names())).toContain('test');
       });
 
       it('should add the `jp-Toolbar-item` class to the widget', () => {
         const item = new Widget();
         widget.addItem('test', item);
-        expect(item.hasClass('jp-Toolbar-item')).to.equal(true);
+        expect(item.hasClass('jp-Toolbar-item')).toBe(true);
       });
 
       it('should return false if the name is already used', () => {
         widget.addItem('test', new Widget());
-        expect(widget.addItem('test', new Widget())).to.equal(false);
+        expect(widget.addItem('test', new Widget())).toBe(false);
       });
     });
 
@@ -79,14 +93,14 @@ describe('@jupyterlab/apputils', () => {
         widget.addItem('a', new Widget());
         widget.addItem('b', new Widget());
         widget.insertItem(1, 'c', new Widget());
-        expect(toArray(widget.names())).to.deep.equal(['a', 'c', 'b']);
+        expect(toArray(widget.names())).toEqual(['a', 'c', 'b']);
       });
 
       it('should clamp the bounds', () => {
         widget.addItem('a', new Widget());
         widget.addItem('b', new Widget());
         widget.insertItem(10, 'c', new Widget());
-        expect(toArray(widget.names())).to.deep.equal(['a', 'b', 'c']);
+        expect(toArray(widget.names())).toEqual(['a', 'b', 'c']);
       });
     });
 
@@ -96,14 +110,14 @@ describe('@jupyterlab/apputils', () => {
         widget.addItem('b', new Widget());
         widget.insertItem(1, 'c', new Widget());
         widget.insertAfter('c', 'd', new Widget());
-        expect(toArray(widget.names())).to.deep.equal(['a', 'c', 'd', 'b']);
+        expect(toArray(widget.names())).toEqual(['a', 'c', 'd', 'b']);
       });
 
       it('should return false if the target item does not exist', () => {
         widget.addItem('a', new Widget());
         widget.addItem('b', new Widget());
         const value = widget.insertAfter('c', 'd', new Widget());
-        expect(value).to.be.false;
+        expect(value).toBe(false);
       });
     });
 
@@ -113,14 +127,14 @@ describe('@jupyterlab/apputils', () => {
         widget.addItem('b', new Widget());
         widget.insertItem(1, 'c', new Widget());
         widget.insertBefore('c', 'd', new Widget());
-        expect(toArray(widget.names())).to.deep.equal(['a', 'd', 'c', 'b']);
+        expect(toArray(widget.names())).toEqual(['a', 'd', 'c', 'b']);
       });
 
       it('should return false if the target item does not exist', () => {
         widget.addItem('a', new Widget());
         widget.addItem('b', new Widget());
         const value = widget.insertBefore('c', 'd', new Widget());
-        expect(value).to.be.false;
+        expect(value).toBe(false);
       });
     });
 
@@ -148,7 +162,7 @@ describe('@jupyterlab/apputils', () => {
       async function render(button: CommandToolbarButton) {
         button.update();
         await framePromise();
-        expect(button.renderPromise).to.exist;
+        expect(button.renderPromise).toBeDefined();
         await button.renderPromise;
       }
 
@@ -157,7 +171,7 @@ describe('@jupyterlab/apputils', () => {
           commands,
           id: testLogCommandId
         });
-        expect(button).to.be.an.instanceof(CommandToolbarButton);
+        expect(button).toBeInstanceOf(CommandToolbarButton);
         button.dispose();
       });
 
@@ -168,7 +182,7 @@ describe('@jupyterlab/apputils', () => {
         });
         await render(button);
         const buttonNode = button.node.firstChild as HTMLButtonElement;
-        expect(buttonNode.classList.contains('test-log-class')).to.equal(true);
+        expect(buttonNode.classList.contains('test-log-class')).toBe(true);
         button.dispose();
       });
 
@@ -179,10 +193,10 @@ describe('@jupyterlab/apputils', () => {
         });
         await render(button);
         const buttonNode = button.node.firstChild as HTMLButtonElement;
-        expect(buttonNode.title).to.equal('Test log command caption');
+        expect(buttonNode.title).toBe('Test log command caption');
         const wrapperNode = buttonNode.firstChild as HTMLElement;
         const iconNode = wrapperNode.firstChild as HTMLElement;
-        expect(iconNode.classList.contains('test-icon-class')).to.equal(true);
+        expect(iconNode.classList.contains('test-icon-class')).toBe(true);
         button.dispose();
       });
 
@@ -196,9 +210,9 @@ describe('@jupyterlab/apputils', () => {
         });
         await render(button);
         const buttonNode = button.node.firstChild as HTMLButtonElement;
-        expect(buttonNode.disabled).to.equal(true);
-        expect(buttonNode.classList.contains('lm-mod-toggled')).to.equal(true);
-        expect(buttonNode.classList.contains('lm-mod-hidden')).to.equal(true);
+        expect(buttonNode.disabled).toBe(true);
+        expect(buttonNode.classList.contains('lm-mod-toggled')).toBe(true);
+        expect(buttonNode.classList.contains('lm-mod-hidden')).toBe(true);
         button.dispose();
       });
 
@@ -212,15 +226,15 @@ describe('@jupyterlab/apputils', () => {
         });
         await render(button);
         const buttonNode = button.node.firstChild as HTMLButtonElement;
-        expect(buttonNode.disabled).to.equal(true);
-        expect(buttonNode.classList.contains('lm-mod-toggled')).to.equal(true);
-        expect(buttonNode.classList.contains('lm-mod-hidden')).to.equal(true);
+        expect(buttonNode.disabled).toBe(true);
+        expect(buttonNode.classList.contains('lm-mod-toggled')).toBe(true);
+        expect(buttonNode.classList.contains('lm-mod-hidden')).toBe(true);
         enabled = true;
         visible = true;
         commands.notifyCommandChanged(testLogCommandId);
-        expect(buttonNode.disabled).to.equal(false);
-        expect(buttonNode.classList.contains('lm-mod-toggled')).to.equal(true);
-        expect(buttonNode.classList.contains('lm-mod-hidden')).to.equal(false);
+        expect(buttonNode.disabled).toBe(false);
+        expect(buttonNode.classList.contains('lm-mod-toggled')).toBe(true);
+        expect(buttonNode.classList.contains('lm-mod-hidden')).toBe(false);
         enabled = false;
         visible = false;
         button.dispose();
@@ -240,7 +254,7 @@ describe('@jupyterlab/apputils', () => {
         });
         await render(button);
         const buttonNode = button.node.firstChild as HTMLButtonElement;
-        expect(buttonNode.textContent).to.equal('Label-only button');
+        expect(buttonNode.textContent).toBe('Label-only button');
         cmd.dispose();
       });
 
@@ -260,15 +274,15 @@ describe('@jupyterlab/apputils', () => {
         });
         await render(button);
         const buttonNode = button.node.firstChild as HTMLButtonElement;
-        expect(buttonNode.textContent).to.equal('Label-only button');
-        expect(buttonNode.classList.contains(iconClassValue)).to.equal(false);
+        expect(buttonNode.textContent).toBe('Label-only button');
+        expect(buttonNode.classList.contains(iconClassValue)).toBe(false);
 
         iconClassValue = 'updated-icon-class';
         commands.notifyCommandChanged(id);
         await render(button);
         const wrapperNode = buttonNode.firstChild as HTMLElement;
         const iconNode = wrapperNode.firstChild as HTMLElement;
-        expect(iconNode.classList.contains(iconClassValue)).to.equal(true);
+        expect(iconNode.classList.contains(iconClassValue)).toBe(true);
         cmd.dispose();
       });
     });
@@ -289,7 +303,9 @@ describe('@jupyterlab/apputils', () => {
           const button = Toolbar.createInterruptButton(sessionContext);
           Widget.attach(button, document.body);
           await framePromise();
-          expect(button.node.querySelector("[data-icon$='stop']")).to.exist;
+          expect(
+            button.node.querySelector("[data-icon$='stop']")
+          ).toBeDefined();
         });
       });
 
@@ -298,7 +314,9 @@ describe('@jupyterlab/apputils', () => {
           const button = Toolbar.createRestartButton(sessionContext);
           Widget.attach(button, document.body);
           await framePromise();
-          expect(button.node.querySelector("[data-icon$='refresh']")).to.exist;
+          expect(
+            button.node.querySelector("[data-icon$='refresh']")
+          ).toBeDefined();
         });
       });
 
@@ -311,7 +329,7 @@ describe('@jupyterlab/apputils', () => {
           const node = item.node.querySelector(
             '.jp-ToolbarButtonComponent-label'
           )!;
-          expect(node.textContent).to.equal(sessionContext.kernelDisplayName);
+          expect(node.textContent).toBe(sessionContext.kernelDisplayName);
         });
       });
 
@@ -326,7 +344,9 @@ describe('@jupyterlab/apputils', () => {
           let called = false;
           sessionContext.statusChanged.connect((_, status) => {
             if (status === 'busy') {
-              expect(item.node.querySelector("[data-icon$='circle']")).to.exist;
+              expect(
+                item.node.querySelector("[data-icon$='circle']")
+              ).toBeDefined();
               called = true;
             }
           });
@@ -334,25 +354,25 @@ describe('@jupyterlab/apputils', () => {
             code: 'a = 109\na'
           })!;
           await future.done;
-          expect(called).to.equal(true);
+          expect(called).toBe(true);
         });
 
         it('should show the current status in the node title', async () => {
           const item = Toolbar.createKernelStatusItem(sessionContext);
           const status = sessionContext.session?.kernel?.status;
-          expect(item.node.title.toLowerCase()).to.contain(status);
+          expect(item.node.title.toLowerCase()).toContain(status);
           let called = false;
           const future = sessionContext.session?.kernel?.requestExecute({
             code: 'a = 1'
           })!;
           future.onIOPub = msg => {
             if (sessionContext.session?.kernel?.status === 'busy') {
-              expect(item.node.title.toLowerCase()).to.contain('busy');
+              expect(item.node.title.toLowerCase()).toContain('busy');
               called = true;
             }
           };
           await future.done;
-          expect(called).to.equal(true);
+          expect(called).toBe(true);
         });
 
         it('should handle a starting session', async () => {
@@ -361,9 +381,10 @@ describe('@jupyterlab/apputils', () => {
           sessionContext = await createSessionContext();
           await sessionContext.initialize();
           const item = Toolbar.createKernelStatusItem(sessionContext);
-          expect(item.node.title).to.equal('Kernel Connecting');
-          expect(item.node.querySelector("[data-icon$='circle-empty']")).to
-            .exist;
+          expect(item.node.title).toBe('Kernel Connecting');
+          expect(
+            item.node.querySelector("[data-icon$='circle-empty']")
+          ).toBeDefined();
           await sessionContext.initialize();
           await sessionContext.session?.kernel?.info;
         });
@@ -375,7 +396,7 @@ describe('@jupyterlab/apputils', () => {
     describe('#constructor()', () => {
       it('should accept no arguments', () => {
         const widget = new ToolbarButton();
-        expect(widget).to.be.an.instanceof(ToolbarButton);
+        expect(widget).toBeInstanceOf(ToolbarButton);
       });
 
       it('should accept options', async () => {
@@ -390,9 +411,9 @@ describe('@jupyterlab/apputils', () => {
         Widget.attach(widget, document.body);
         await framePromise();
         const button = widget.node.firstChild as HTMLElement;
-        expect(button.classList.contains('foo')).to.equal(true);
-        expect(button.querySelector('.iconFoo')).to.exist;
-        expect(button.title).to.equal('bar');
+        expect(button.classList.contains('foo')).toBe(true);
+        expect(button.querySelector('.iconFoo')).toBeDefined();
+        expect(button.title).toBe('bar');
       });
     });
 
@@ -400,14 +421,14 @@ describe('@jupyterlab/apputils', () => {
       it('should dispose of the resources used by the widget', () => {
         const button = new ToolbarButton();
         button.dispose();
-        expect(button.isDisposed).to.equal(true);
+        expect(button.isDisposed).toBe(true);
       });
 
       it('should be safe to call more than once', () => {
         const button = new ToolbarButton();
         button.dispose();
         button.dispose();
-        expect(button.isDisposed).to.equal(true);
+        expect(button.isDisposed).toBe(true);
       });
     });
 
@@ -423,7 +444,7 @@ describe('@jupyterlab/apputils', () => {
           Widget.attach(button, document.body);
           await framePromise();
           simulate(button.node.firstChild as HTMLElement, 'mousedown');
-          expect(called).to.equal(true);
+          expect(called).toBe(true);
           button.dispose();
         });
       });
@@ -440,7 +461,7 @@ describe('@jupyterlab/apputils', () => {
           simulate(button.node.firstChild as HTMLElement, 'keydown', {
             key: 'Enter'
           });
-          expect(called).to.equal(true);
+          expect(called).toBe(true);
           button.dispose();
         });
         it('Space should activate the callback', async () => {
@@ -455,7 +476,7 @@ describe('@jupyterlab/apputils', () => {
           simulate(button.node.firstChild as HTMLElement, 'keydown', {
             key: ' '
           });
-          expect(called).to.equal(true);
+          expect(called).toBe(true);
           button.dispose();
         });
       });

+ 10 - 11
tests/test-apputils/src/vdom.spec.ts → packages/apputils/test/vdom.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 { VDomModel, VDomRenderer } from '@jupyterlab/apputils';
 
@@ -41,18 +40,18 @@ describe('@jupyterlab/apputils', () => {
     describe('#constructor()', () => {
       it('should create a VDomModel', () => {
         const model = new VDomModel();
-        expect(model).to.be.an.instanceof(VDomModel);
+        expect(model).toBeInstanceOf(VDomModel);
       });
 
       it('should create a TestModel', () => {
         const model = new TestModel();
-        expect(model).to.be.an.instanceof(TestModel);
+        expect(model).toBeInstanceOf(TestModel);
       });
 
       it('should be properly disposed', () => {
         const model = new TestModel();
         model.dispose();
-        expect(model.isDisposed).to.be.equal(true);
+        expect(model.isDisposed).toBe(true);
       });
     });
 
@@ -64,7 +63,7 @@ describe('@jupyterlab/apputils', () => {
           changed = true;
         });
         model.value = 'newvalue';
-        expect(changed).to.equal(true);
+        expect(changed).toBe(true);
       });
     });
   });
@@ -73,13 +72,13 @@ describe('@jupyterlab/apputils', () => {
     describe('#constructor()', () => {
       it('should create a TestWidget', () => {
         const widget = new TestWidget(new TestModel());
-        expect(widget).to.be.an.instanceof(TestWidget);
+        expect(widget).toBeInstanceOf(TestWidget);
       });
 
       it('should be properly disposed', () => {
         const widget = new TestWidget(new TestModel());
         widget.dispose();
-        expect(widget.isDisposed).to.equal(true);
+        expect(widget.isDisposed).toBe(true);
       });
     });
 
@@ -92,7 +91,7 @@ describe('@jupyterlab/apputils', () => {
           changed = true;
         });
         widget.model = model;
-        expect(changed).to.equal(true);
+        expect(changed).toBe(true);
       });
     });
 
@@ -104,7 +103,7 @@ describe('@jupyterlab/apputils', () => {
         model.value = 'foo';
         await framePromise();
         const span = widget.node.firstChild as HTMLElement;
-        expect(span.textContent).to.equal('foo');
+        expect(span.textContent).toBe('foo');
       });
     });
 
@@ -114,7 +113,7 @@ describe('@jupyterlab/apputils', () => {
         Widget.attach(widget, document.body);
         await framePromise();
         const span = widget.node.firstChild as HTMLElement;
-        expect(span.textContent).to.equal('No model!');
+        expect(span.textContent).toBe('No model!');
       });
     });
   });

+ 47 - 46
tests/test-apputils/src/widgettracker.spec.ts → packages/apputils/test/widgettracker.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 { WidgetTracker } from '@jupyterlab/apputils';
 
@@ -50,7 +49,7 @@ describe('@jupyterlab/apputils', () => {
 
     describe('#constructor()', () => {
       it('should create an WidgetTracker', () => {
-        expect(tracker).to.be.an.instanceof(WidgetTracker);
+        expect(tracker).toBeInstanceOf(WidgetTracker);
       });
     });
 
@@ -85,7 +84,7 @@ describe('@jupyterlab/apputils', () => {
           called = true;
         });
         await tracker.add(widget2);
-        expect(called).to.equal(false);
+        expect(called).toBe(false);
         widget.dispose();
         widget2.dispose();
       });
@@ -115,8 +114,8 @@ describe('@jupyterlab/apputils', () => {
 
         const [sender, args] = await promise;
 
-        expect(sender).to.equal(tracker);
-        expect(args).to.equal(widget);
+        expect(sender).toBe(tracker);
+        expect(args).toBe(widget);
         widget.dispose();
       });
 
@@ -146,14 +145,14 @@ describe('@jupyterlab/apputils', () => {
 
     describe('#currentWidget', () => {
       it('should default to null', () => {
-        expect(tracker.currentWidget).to.be.null;
+        expect(tracker.currentWidget).toBeNull();
       });
 
       it('should be updated when a widget is added', async () => {
         const widget = createWidget();
 
         await tracker.add(widget);
-        expect(tracker.currentWidget).to.equal(widget);
+        expect(tracker.currentWidget).toBe(widget);
         widget.dispose();
       });
 
@@ -167,9 +166,9 @@ describe('@jupyterlab/apputils', () => {
         panel.addWidget(widget0);
         panel.addWidget(widget1);
         Widget.attach(panel, document.body);
-        expect(tracker.currentWidget).to.equal(widget1);
+        expect(tracker.currentWidget).toBe(widget1);
         focus(widget0);
-        expect(tracker.currentWidget).to.equal(widget0);
+        expect(tracker.currentWidget).toBe(widget0);
         panel.dispose();
         widget0.dispose();
         widget1.dispose();
@@ -183,9 +182,9 @@ describe('@jupyterlab/apputils', () => {
         await tracker.add(two);
         focus(one);
         focus(two);
-        expect(tracker.currentWidget).to.equal(two);
+        expect(tracker.currentWidget).toBe(two);
         two.dispose();
-        expect(tracker.currentWidget).to.equal(one);
+        expect(tracker.currentWidget).toBe(one);
         one.dispose();
       });
 
@@ -200,15 +199,15 @@ describe('@jupyterlab/apputils', () => {
         Widget.attach(panel, document.body);
 
         focus(widgets[0]);
-        expect(tracker.currentWidget).to.equal(widgets[0]);
+        expect(tracker.currentWidget).toBe(widgets[0]);
 
         let called = false;
         tracker.currentChanged.connect(() => {
           called = true;
         });
         widgets[2].dispose();
-        expect(tracker.currentWidget).to.equal(widgets[0]);
-        expect(called).to.equal(false);
+        expect(tracker.currentWidget).toBe(widgets[0]);
+        expect(called).toBe(false);
         panel.dispose();
         widgets.forEach(widget => {
           widget.dispose();
@@ -231,8 +230,8 @@ describe('@jupyterlab/apputils', () => {
           called = true;
         });
         widgets[2].dispose();
-        expect(tracker.currentWidget).to.equal(widgets[1]);
-        expect(called).to.equal(true);
+        expect(tracker.currentWidget).toBe(widgets[1]);
+        expect(called).toBe(true);
         panel.dispose();
         widgets.forEach(widget => {
           widget.dispose();
@@ -242,71 +241,71 @@ describe('@jupyterlab/apputils', () => {
 
     describe('#isDisposed', () => {
       it('should test whether the tracker is disposed', () => {
-        expect(tracker.isDisposed).to.equal(false);
+        expect(tracker.isDisposed).toBe(false);
         tracker.dispose();
-        expect(tracker.isDisposed).to.equal(true);
+        expect(tracker.isDisposed).toBe(true);
       });
     });
 
     describe('#add()', () => {
       it('should add a widget to the tracker', async () => {
         const widget = createWidget();
-        expect(tracker.has(widget)).to.equal(false);
+        expect(tracker.has(widget)).toBe(false);
         await tracker.add(widget);
-        expect(tracker.has(widget)).to.equal(true);
+        expect(tracker.has(widget)).toBe(true);
         widget.dispose();
       });
 
       it('should reject a widget that already exists', async () => {
         const widget = createWidget();
         let failed = false;
-        expect(tracker.has(widget)).to.equal(false);
+        expect(tracker.has(widget)).toBe(false);
         await tracker.add(widget);
-        expect(tracker.has(widget)).to.equal(true);
+        expect(tracker.has(widget)).toBe(true);
         try {
           await tracker.add(widget);
         } catch (error) {
           failed = true;
         }
-        expect(failed).to.equal(true);
+        expect(failed).toBe(true);
         widget.dispose();
       });
 
       it('should reject a widget that is disposed', async () => {
         const widget = createWidget();
         let failed = false;
-        expect(tracker.has(widget)).to.equal(false);
+        expect(tracker.has(widget)).toBe(false);
         widget.dispose();
         try {
           await tracker.add(widget);
         } catch (error) {
           failed = true;
         }
-        expect(failed).to.equal(true);
+        expect(failed).toBe(true);
         widget.dispose();
       });
 
       it('should remove an added widget if it is disposed', async () => {
         const widget = createWidget();
         await tracker.add(widget);
-        expect(tracker.has(widget)).to.equal(true);
+        expect(tracker.has(widget)).toBe(true);
         widget.dispose();
-        expect(tracker.has(widget)).to.equal(false);
+        expect(tracker.has(widget)).toBe(false);
       });
     });
 
     describe('#dispose()', () => {
       it('should dispose of the resources used by the tracker', () => {
-        expect(tracker.isDisposed).to.equal(false);
+        expect(tracker.isDisposed).toBe(false);
         tracker.dispose();
-        expect(tracker.isDisposed).to.equal(true);
+        expect(tracker.isDisposed).toBe(true);
       });
 
       it('should be safe to call multiple times', () => {
-        expect(tracker.isDisposed).to.equal(false);
+        expect(tracker.isDisposed).toBe(false);
         tracker.dispose();
         tracker.dispose();
-        expect(tracker.isDisposed).to.equal(true);
+        expect(tracker.isDisposed).toBe(true);
       });
     });
 
@@ -321,7 +320,7 @@ describe('@jupyterlab/apputils', () => {
         void tracker.add(widgetA);
         void tracker.add(widgetB);
         void tracker.add(widgetC);
-        expect(tracker.find(widget => widget.id === 'B')).to.equal(widgetB);
+        expect(tracker.find(widget => widget.id === 'B')).toBe(widgetB);
         widgetA.dispose();
         widgetB.dispose();
         widgetC.dispose();
@@ -337,7 +336,7 @@ describe('@jupyterlab/apputils', () => {
         void tracker.add(widgetA);
         void tracker.add(widgetB);
         void tracker.add(widgetC);
-        expect(tracker.find(widget => widget.id === 'D')).to.not.be.ok;
+        expect(tracker.find(widget => widget.id === 'D')).toBeFalsy();
         widgetA.dispose();
         widgetB.dispose();
         widgetC.dispose();
@@ -358,9 +357,9 @@ describe('@jupyterlab/apputils', () => {
         const list = tracker.filter(
           widget => widget.id.indexOf('include') !== -1
         );
-        expect(list.length).to.equal(2);
-        expect(list[0]).to.equal(widgetA);
-        expect(list[1]).to.equal(widgetB);
+        expect(list.length).toBe(2);
+        expect(list[0]).toBe(widgetA);
+        expect(list[1]).toBe(widgetB);
         widgetA.dispose();
         widgetB.dispose();
         widgetC.dispose();
@@ -376,7 +375,7 @@ describe('@jupyterlab/apputils', () => {
         void tracker.add(widgetA);
         void tracker.add(widgetB);
         void tracker.add(widgetC);
-        expect(tracker.filter(widget => widget.id === 'D').length).to.equal(0);
+        expect(tracker.filter(widget => widget.id === 'D').length).toBe(0);
         widgetA.dispose();
         widgetB.dispose();
         widgetC.dispose();
@@ -398,7 +397,7 @@ describe('@jupyterlab/apputils', () => {
         tracker.forEach(widget => {
           visited += widget.id;
         });
-        expect(visited).to.equal('ABC');
+        expect(visited).toBe('ABC');
         widgetA.dispose();
         widgetB.dispose();
         widgetC.dispose();
@@ -408,9 +407,9 @@ describe('@jupyterlab/apputils', () => {
     describe('#has()', () => {
       it('should return `true` if an item exists in the tracker', () => {
         const widget = createWidget();
-        expect(tracker.has(widget)).to.equal(false);
+        expect(tracker.has(widget)).toBe(false);
         void tracker.add(widget);
-        expect(tracker.has(widget)).to.equal(true);
+        expect(tracker.has(widget)).toBe(true);
         widget.dispose();
       });
     });
@@ -418,18 +417,18 @@ describe('@jupyterlab/apputils', () => {
     describe('#inject()', () => {
       it('should inject a widget into the tracker', async () => {
         const widget = createWidget();
-        expect(tracker.has(widget)).to.equal(false);
+        expect(tracker.has(widget)).toBe(false);
         void tracker.inject(widget);
-        expect(tracker.has(widget)).to.equal(true);
+        expect(tracker.has(widget)).toBe(true);
         widget.dispose();
       });
 
       it('should remove an injected widget if it is disposed', async () => {
         const widget = createWidget();
         void tracker.inject(widget);
-        expect(tracker.has(widget)).to.equal(true);
+        expect(tracker.has(widget)).toBe(true);
         widget.dispose();
-        expect(tracker.has(widget)).to.equal(false);
+        expect(tracker.has(widget)).toBe(false);
       });
     });
 
@@ -438,7 +437,9 @@ describe('@jupyterlab/apputils', () => {
         const tracker = new TestTracker({ namespace });
         const widget = createWidget();
         await tracker.add(widget);
-        expect(tracker.methods).to.contain('onCurrentChanged');
+        expect(tracker.methods).toEqual(
+          expect.arrayContaining(['onCurrentChanged'])
+        );
         widget.dispose();
       });
     });

+ 39 - 0
packages/apputils/tsconfig.test.json

@@ -0,0 +1,39 @@
+{
+  "extends": "../../tsconfigbase.test",
+  "include": ["src/*", "test/*"],
+  "references": [
+    {
+      "path": "../coreutils"
+    },
+    {
+      "path": "../services"
+    },
+    {
+      "path": "../settingregistry"
+    },
+    {
+      "path": "../statedb"
+    },
+    {
+      "path": "../ui-components"
+    },
+    {
+      "path": "../../testutils"
+    },
+    {
+      "path": "../coreutils"
+    },
+    {
+      "path": "../services"
+    },
+    {
+      "path": "../settingregistry"
+    },
+    {
+      "path": "../statedb"
+    },
+    {
+      "path": "../ui-components"
+    }
+  ]
+}

+ 0 - 1
packages/services/package.json

@@ -66,7 +66,6 @@
     "@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",

+ 5 - 3
packages/services/test/kernel/comm.spec.ts

@@ -2,13 +2,15 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { PromiseDelegate } from '@lumino/coreutils';
 
 import { KernelMessage, Kernel, KernelManager } from '../../src';
 
-import { isFulfilled, JupyterServer } from '@jupyterlab/testutils';
+import {
+  isFulfilled,
+  JupyterServer,
+  flakyIt as it
+} from '@jupyterlab/testutils';
 
 import { init } from '../utils';
 

+ 1 - 3
packages/services/test/kernel/ifuture.spec.ts

@@ -2,12 +2,10 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { Kernel, KernelMessage, KernelAPI, KernelManager } from '../../src';
 
 import { KernelTester } from '../utils';
-import { JupyterServer } from '@jupyterlab/testutils';
+import { JupyterServer, flakyIt as it } from '@jupyterlab/testutils';
 
 const server = new JupyterServer();
 

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

@@ -2,8 +2,6 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { PageConfig } from '@jupyterlab/coreutils';
 
 import { UUID } from '@lumino/coreutils';
@@ -22,7 +20,8 @@ import {
 import {
   expectFailure,
   testEmission,
-  JupyterServer
+  JupyterServer,
+  flakyIt as it
 } from '@jupyterlab/testutils';
 
 import { KernelTester, handleRequest } from '../utils';

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

@@ -2,8 +2,6 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { UUID } from '@lumino/coreutils';
 
 import { toArray } from '@lumino/algorithm';
@@ -13,7 +11,8 @@ import { KernelAPI } from '../../src';
 import {
   expectFailure,
   testEmission,
-  JupyterServer
+  JupyterServer,
+  flakyIt as it
 } from '@jupyterlab/testutils';
 
 import {

+ 6 - 3
packages/services/test/kernel/manager.spec.ts

@@ -2,13 +2,16 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { toArray } from '@lumino/algorithm';
 
 import { KernelManager, Kernel, KernelAPI } from '../../src';
 
-import { testEmission, sleep, JupyterServer } from '@jupyterlab/testutils';
+import {
+  testEmission,
+  sleep,
+  JupyterServer,
+  flakyIt as it
+} from '@jupyterlab/testutils';
 
 import { makeSettings } from '../utils';
 

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

@@ -2,8 +2,6 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { PageConfig } from '@jupyterlab/coreutils';
 
 import { UUID } from '@lumino/coreutils';
@@ -21,7 +19,8 @@ import {
 import {
   expectFailure,
   testEmission,
-  JupyterServer
+  JupyterServer,
+  flakyIt as it
 } from '@jupyterlab/testutils';
 
 import { handleRequest, SessionTester, init } from '../utils';

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

@@ -2,8 +2,6 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { UUID } from '@lumino/coreutils';
 
 import { toArray } from '@lumino/algorithm';
@@ -16,7 +14,11 @@ import {
   KernelManager
 } from '../../src';
 
-import { testEmission, JupyterServer } from '@jupyterlab/testutils';
+import {
+  testEmission,
+  JupyterServer,
+  flakyIt as it
+} from '@jupyterlab/testutils';
 
 /**
  * Start a new session on with a default name.

+ 5 - 3
packages/services/test/session/session.spec.ts

@@ -2,8 +2,6 @@
 
 import 'jest';
 
-const it = require('jest-retries');
-
 import { UUID } from '@lumino/coreutils';
 
 import { toArray } from '@lumino/algorithm';
@@ -12,7 +10,11 @@ import { SessionAPI } from '../../src';
 
 import { Session } from '../../src';
 
-import { expectFailure, JupyterServer } from '@jupyterlab/testutils';
+import {
+  expectFailure,
+  JupyterServer,
+  flakyIt as it
+} from '@jupyterlab/testutils';
 
 import {
   makeSettings,

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

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

+ 0 - 37
tests/test-apputils/package.json

@@ -1,37 +0,0 @@
-{
-  "name": "@jupyterlab/test-apputils",
-  "version": "2.1.0",
-  "private": true,
-  "scripts": {
-    "build": "tsc -b",
-    "clean": "rimraf build && rimraf coverage",
-    "coverage": "python run.py --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/services": "^5.1.0",
-    "@jupyterlab/testutils": "^2.1.0",
-    "@lumino/algorithm": "^1.2.3",
-    "@lumino/commands": "^1.10.1",
-    "@lumino/coreutils": "^1.4.2",
-    "@lumino/messaging": "^1.3.3",
-    "@lumino/virtualdom": "^1.6.1",
-    "@lumino/widgets": "^1.11.1",
-    "chai": "^4.2.0",
-    "jest": "^25.2.3",
-    "jest-junit": "^10.0.0",
-    "react": "~16.9.0",
-    "simulate-event": "~1.4.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-apputils/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 - 21
tests/test-apputils/tsconfig.json

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

+ 1 - 0
testutils/package.json

@@ -60,6 +60,7 @@
   "devDependencies": {
     "@types/jest": "^24.0.23",
     "@types/node-fetch": "^2.5.4",
+    "jest-retries": "^1.0.1",
     "lighthouse": "5.6.0",
     "typescript": "~3.7.3"
   },

+ 4 - 0
testutils/src/index.ts

@@ -24,6 +24,10 @@ export { defaultRenderMime } from './rendermime';
 
 export { JupyterServer } from './start_jupyter_server';
 
+const jestRetries = require('jest-retries');
+
+export const flakyIt: jest.It = jestRetries;
+
 /**
  * Test a single emission from a signal.
  *