Browse Source

wip update rendermime tests

Steven Silvester 5 years ago
parent
commit
58fca2438e

+ 0 - 1
dev_mode/package.json

@@ -381,7 +381,6 @@
       "@jupyterlab/test-notebook": "../tests/test-notebook",
       "@jupyterlab/test-observables": "../tests/test-observables",
       "@jupyterlab/test-outputarea": "../tests/test-outputarea",
-      "@jupyterlab/test-rendermime": "../tests/test-rendermime",
       "@jupyterlab/test-services": "../tests/test-services",
       "@jupyterlab/test-settingregistry": "../tests/test-settingregistry",
       "@jupyterlab/test-statedb": "../tests/test-statedb",

+ 3 - 2
packages/docregistry/src/context.ts

@@ -39,8 +39,9 @@ import { DocumentRegistry } from './registry';
  *
  * This class is typically instantiated by the document manager.
  */
-export class Context<T extends DocumentRegistry.IModel>
-  implements DocumentRegistry.IContext<T> {
+export class Context<
+  T extends DocumentRegistry.IModel = DocumentRegistry.IModel
+> implements DocumentRegistry.IContext<T> {
   /**
    * Construct a new document context.
    */

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

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

+ 1 - 0
packages/rendermime/babel.config.js

@@ -0,0 +1 @@
+module.exports = require('@jupyterlab/testutils/lib/babel.config');

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

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

+ 9 - 0
packages/rendermime/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": {
@@ -51,9 +56,13 @@
     "marked": "^0.8.0"
   },
   "devDependencies": {
+    "@jupyterlab/testutils": "^2.1.0",
+    "@types/jest": "^24.0.23",
     "@types/lodash.escape": "^4.0.6",
     "@types/marked": "^0.7.2",
+    "jest": "^25.2.3",
     "rimraf": "~3.0.0",
+    "ts-jest": "^25.2.1",
     "typedoc": "^0.15.4",
     "typescript": "~3.7.3"
   },

+ 52 - 47
tests/test-rendermime/src/factories.spec.ts → packages/rendermime/test/factories.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 { JSONObject, JSONValue } from '@lumino/coreutils';
 
@@ -16,9 +15,9 @@ import {
   textRendererFactory,
   htmlRendererFactory,
   imageRendererFactory
-} from '@jupyterlab/rendermime';
+} from '../src';
 
-import { MimeModel, IRenderMime } from '@jupyterlab/rendermime';
+import { MimeModel, IRenderMime } from '../src';
 
 function createModel(
   mimeType: string,
@@ -46,13 +45,13 @@ describe('rendermime/factories', () => {
           'application/vnd.jupyter.stdout',
           'application/vnd.jupyter.stderr'
         ];
-        expect(textRendererFactory.mimeTypes).to.deep.equal(mimeTypes);
+        expect(textRendererFactory.mimeTypes).toEqual(mimeTypes);
       });
     });
 
     describe('#safe', () => {
       it('should be safe', () => {
-        expect(textRendererFactory.safe).to.equal(true);
+        expect(textRendererFactory.safe).toBe(true);
       });
     });
 
@@ -63,7 +62,7 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, 'x = 2 ** a');
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal('<pre>x = 2 ** a</pre>');
+        expect(w.node.innerHTML).toBe('<pre>x = 2 ** a</pre>');
       });
 
       it('should be re-renderable', async () => {
@@ -73,7 +72,7 @@ describe('rendermime/factories', () => {
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal('<pre>x = 2 ** a</pre>');
+        expect(w.node.innerHTML).toBe('<pre>x = 2 ** a</pre>');
       });
 
       it('should output the correct HTML with ansi colors', async () => {
@@ -83,7 +82,7 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal(
+        expect(w.node.innerHTML).toBe(
           '<pre>There is no text but <span class="ansi-green-intense-fg ansi-red-bg ansi-bold">text</span>.\nWoo.</pre>'
         );
       });
@@ -96,7 +95,7 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal(
+        expect(w.node.innerHTML).toBe(
           '<pre>There is no text &lt;script&gt;window.x=1&lt;/script&gt; but <span class="ansi-green-intense-fg ansi-red-bg ansi-bold">text</span>.\nWoo.</pre>'
         );
       });
@@ -118,7 +117,7 @@ describe('rendermime/factories', () => {
             const model = createModel(mimeType, source);
             const w = f.createRenderer({ mimeType, ...defaultOptions });
             await w.renderModel(model);
-            expect(w.node.innerHTML).to.equal(
+            expect(w.node.innerHTML).toBe(
               `<pre>Here is some text with an URL such as <a href="${url}" rel="noopener" target="_blank">${url}</a> inside.</pre>`
             );
           })
@@ -130,13 +129,13 @@ describe('rendermime/factories', () => {
   describe('latexRendererFactory', () => {
     describe('#mimeTypes', () => {
       it('should have the text/latex mimeType', () => {
-        expect(latexRendererFactory.mimeTypes).to.deep.equal(['text/latex']);
+        expect(latexRendererFactory.mimeTypes).toEqual(['text/latex']);
       });
     });
 
     describe('#safe', () => {
       it('should be safe', () => {
-        expect(latexRendererFactory.safe).to.equal(true);
+        expect(latexRendererFactory.safe).toBe(true);
       });
     });
 
@@ -148,7 +147,7 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.textContent).to.equal(source);
+        expect(w.node.textContent).toBe(source);
       });
 
       it('should be re-renderable', async () => {
@@ -159,7 +158,7 @@ describe('rendermime/factories', () => {
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
         await w.renderModel(model);
-        expect(w.node.textContent).to.equal(source);
+        expect(w.node.textContent).toBe(source);
       });
     });
   });
@@ -167,13 +166,13 @@ describe('rendermime/factories', () => {
   describe('svgRendererFactory', () => {
     describe('#mimeTypes', () => {
       it('should have the image/svg+xml mimeType', () => {
-        expect(svgRendererFactory.mimeTypes).to.deep.equal(['image/svg+xml']);
+        expect(svgRendererFactory.mimeTypes).toEqual(['image/svg+xml']);
       });
     });
 
     describe('#safe', () => {
       it('should not be safe', () => {
-        expect(svgRendererFactory.safe).to.equal(false);
+        expect(svgRendererFactory.safe).toBe(false);
       });
     });
 
@@ -187,8 +186,10 @@ describe('rendermime/factories', () => {
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
         const imgEl = w.node.getElementsByTagName('img')[0];
-        expect(imgEl).to.be.ok;
-        expect(imgEl.src).to.contain(encodeURIComponent(displaySource));
+        expect(imgEl).toBeTruthy();
+        expect(imgEl.src).toEqual(
+          expect.arrayContaining([encodeURIComponent(displaySource)])
+        );
       });
     });
   });
@@ -196,15 +197,13 @@ describe('rendermime/factories', () => {
   describe('markdownRendererFactory', () => {
     describe('#mimeTypes', () => {
       it('should have the text/markdown mimeType', function() {
-        expect(markdownRendererFactory.mimeTypes).to.deep.equal([
-          'text/markdown'
-        ]);
+        expect(markdownRendererFactory.mimeTypes).toEqual(['text/markdown']);
       });
     });
 
     describe('#safe', () => {
       it('should be safe', () => {
-        expect(markdownRendererFactory.safe).to.equal(true);
+        expect(markdownRendererFactory.safe).toBe(true);
       });
     });
 
@@ -216,7 +215,7 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal(source);
+        expect(w.node.innerHTML).toBe(source);
       });
 
       it('it should be re-renderable', async () => {
@@ -227,7 +226,7 @@ describe('rendermime/factories', () => {
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal(source);
+        expect(w.node.innerHTML).toBe(source);
       });
 
       it('should add header anchors', async () => {
@@ -240,12 +239,16 @@ describe('rendermime/factories', () => {
         await w.renderModel(model);
         Widget.attach(w, document.body);
         const node = document.getElementById('Title-third-level')!;
-        expect(node.localName).to.equal('h3');
+        expect(node.localName).toBe('h3');
         const anchor = node.firstChild!.nextSibling as HTMLAnchorElement;
-        expect(anchor.href).to.contain('#Title-third-level');
-        expect(anchor.target).to.equal('_self');
-        expect(anchor.className).to.contain('jp-InternalAnchorLink');
-        expect(anchor.textContent).to.equal('¶');
+        expect(anchor.href).toEqual(
+          expect.arrayContaining(['#Title-third-level'])
+        );
+        expect(anchor.target).toBe('_self');
+        expect(anchor.className).toEqual(
+          expect.arrayContaining(['jp-InternalAnchorLink'])
+        );
+        expect(anchor.textContent).toBe('¶');
         Widget.detach(w);
       });
 
@@ -256,7 +259,9 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.not.contain('script');
+        expect(w.node.innerHTML).toEqual(
+          expect.not.arrayContaining(['script'])
+        );
       });
     });
   });
@@ -264,13 +269,13 @@ describe('rendermime/factories', () => {
   describe('htmlRendererFactory', () => {
     describe('#mimeTypes', () => {
       it('should have the text/html mimeType', () => {
-        expect(htmlRendererFactory.mimeTypes).to.deep.equal(['text/html']);
+        expect(htmlRendererFactory.mimeTypes).toEqual(['text/html']);
       });
     });
 
     describe('#safe', () => {
       it('should be safe', () => {
-        expect(htmlRendererFactory.safe).to.equal(true);
+        expect(htmlRendererFactory.safe).toBe(true);
       });
     });
 
@@ -282,7 +287,7 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal('<h1>This is great</h1>');
+        expect(w.node.innerHTML).toBe('<h1>This is great</h1>');
       });
 
       it('should be re-renderable', async () => {
@@ -293,7 +298,7 @@ describe('rendermime/factories', () => {
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal('<h1>This is great</h1>');
+        expect(w.node.innerHTML).toBe('<h1>This is great</h1>');
       });
 
       // TODO we are disabling script execution for now.
@@ -304,9 +309,9 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source, true);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         return w.renderModel(model).then(() => {
-          expect((window as any).y).to.be.undefined;
+          expect((window as any).y).toBeUndefined();
           Widget.attach(w, document.body);
-          expect((window as any).y).to.equal(3);
+          expect((window as any).y).toBe(3);
           w.dispose();
         });
       });
@@ -318,7 +323,7 @@ describe('rendermime/factories', () => {
         const model = createModel(mimeType, source);
         const w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
-        expect(w.node.innerHTML).to.equal('<pre></pre>');
+        expect(w.node.innerHTML).toBe('<pre></pre>');
       });
     });
 
@@ -331,14 +336,14 @@ describe('rendermime/factories', () => {
       const mimeType = 'text/html';
       const w = f.createRenderer({ mimeType, ...defaultOptions });
       await w.renderModel(model);
-      expect(w.node.innerHTML).to.equal('<h1>foo </h1>');
+      expect(w.node.innerHTML).toBe('<h1>foo </h1>');
     });
   });
 
   describe('imageRendererFactory', () => {
     describe('#mimeTypes', () => {
       it('should support multiple mimeTypes', () => {
-        expect(imageRendererFactory.mimeTypes).to.deep.equal([
+        expect(imageRendererFactory.mimeTypes).toEqual([
           'image/bmp',
           'image/png',
           'image/jpeg',
@@ -349,7 +354,7 @@ describe('rendermime/factories', () => {
 
     describe('#safe', () => {
       it('should be safe', () => {
-        expect(imageRendererFactory.safe).to.equal(true);
+        expect(imageRendererFactory.safe).toBe(true);
       });
     });
 
@@ -363,9 +368,9 @@ describe('rendermime/factories', () => {
 
         await w.renderModel(model);
         let el = w.node.firstChild as HTMLImageElement;
-        expect(el.src).to.equal('data:image/png;base64,' + source);
-        expect(el.localName).to.equal('img');
-        expect(el.innerHTML).to.equal('');
+        expect(el.src).toBe('data:image/png;base64,' + source);
+        expect(el.localName).toBe('img');
+        expect(el.innerHTML).toBe('');
 
         source = 'R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=';
         mimeType = 'image/gif';
@@ -373,9 +378,9 @@ describe('rendermime/factories', () => {
         w = f.createRenderer({ mimeType, ...defaultOptions });
         await w.renderModel(model);
         el = w.node.firstChild as HTMLImageElement;
-        expect(el.src).to.equal('data:image/gif;base64,' + source);
-        expect(el.localName).to.equal('img');
-        expect(el.innerHTML).to.equal('');
+        expect(el.src).toBe('data:image/gif;base64,' + source);
+        expect(el.localName).toBe('img');
+        expect(el.innerHTML).toBe('');
       });
     });
   });

+ 23 - 24
tests/test-rendermime/src/latex.spec.ts → packages/rendermime/test/latex.spec.ts

@@ -1,59 +1,58 @@
 // Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
 
-import { expect } from 'chai';
+import 'jest';
 
-import { removeMath, replaceMath } from '@jupyterlab/rendermime';
+import { removeMath, replaceMath } from '../src';
 
 describe('jupyter-ui', () => {
   describe('removeMath()', () => {
     it('should split the text into text and math', () => {
       const input = 'hello, $ /alpha $, there';
       const { text, math } = removeMath(input);
-      expect(text).to.equal('hello, @@0@@, there');
-      expect(math).to.deep.equal(['$ /alpha $']);
+      expect(text).toBe('hello, @@0@@, there');
+      expect(math).toEqual(['$ /alpha $']);
     });
 
     it('should handle code spans', () => {
       const input = '`$foo` and `$bar` are variables';
       const { text, math } = removeMath(input);
-      expect(text).to.equal(input);
-      expect(math).to.deep.equal([]);
+      expect(text).toBe(input);
+      expect(math).toEqual([]);
     });
 
     it('should handle math markers', () => {
       const input = ' @@0@@ hello, $ /alpha $, there';
       const { text, math } = removeMath(input);
-      expect(text).to.equal(' @@0@@ hello, @@1@@, there');
-      expect(math).to.deep.equal(['@@0@@', '$ /alpha $']);
+      expect(text).toBe(' @@0@@ hello, @@1@@, there');
+      expect(math).toEqual(['@@0@@', '$ /alpha $']);
     });
 
     it('should handle unbalanced braces', () => {
       const input = 'hello, $ /alpha { $, there';
       const { text, math } = removeMath(input);
-      expect(text).to.equal('hello, @@0@@, there');
-      expect(math).to.deep.equal(['$ /alpha { $']);
+      expect(text).toBe('hello, @@0@@, there');
+      expect(math).toEqual(['$ /alpha { $']);
     });
 
     it('should handle balanced braces', () => {
       const input = 'hello, $ /alpha { } $, there';
       const { text, math } = removeMath(input);
-      expect(text).to.equal('hello, @@0@@, there');
-      expect(math).to.deep.equal(['$ /alpha { } $']);
+      expect(text).toBe('hello, @@0@@, there');
+      expect(math).toEqual(['$ /alpha { } $']);
     });
 
     it('should handle math blocks', () => {
       const input = 'hello, $$\nfoo\n$$, there';
       const { text, math } = removeMath(input);
-      expect(text).to.equal('hello, @@0@@, there');
-      expect(math).to.deep.equal(['$$\nfoo\n$$']);
+      expect(text).toBe('hello, @@0@@, there');
+      expect(math).toEqual(['$$\nfoo\n$$']);
     });
 
     it('should handle begin statements', () => {
       const input = 'hello, \\begin{align} \\end{align}, there';
       const { text, math } = removeMath(input);
-      expect(text).to.equal('hello, @@0@@, there');
-      expect(math).to.deep.equal(['\\begin{align} \\end{align}']);
+      expect(text).toBe('hello, @@0@@, there');
+      expect(math).toEqual(['\\begin{align} \\end{align}']);
     });
 
     it('should handle `\\(` delimiters in GFM', () => {
@@ -65,8 +64,8 @@ describe('jupyter-ui', () => {
         ## header
       `;
       const { text, math } = removeMath(input);
-      expect(text).to.equal(input);
-      expect(math).to.deep.equal([]);
+      expect(text).toBe(input);
+      expect(math).toEqual([]);
     });
 
     it('should handle `\\\\(` delimiters for math', () => {
@@ -74,8 +73,8 @@ describe('jupyter-ui', () => {
           /alpha
       \\\\\), there`;
       const { text, math } = removeMath(input);
-      expect(text).to.equal('hello, @@0@@, there');
-      expect(math).to.deep.equal(['\\\\(\n          /alpha\n      \\\\)']);
+      expect(text).toBe('hello, @@0@@, there');
+      expect(math).toEqual(['\\\\(\n          /alpha\n      \\\\)']);
     });
 
     it('should handle `\\\\[` delimiters for math', () => {
@@ -83,8 +82,8 @@ describe('jupyter-ui', () => {
           /alpha
       \\\\\], there`;
       const { text, math } = removeMath(input);
-      expect(text).to.equal('hello, @@0@@, there');
-      expect(math).to.deep.equal(['\\\\[\n          /alpha\n      \\\\]']);
+      expect(text).toBe('hello, @@0@@, there');
+      expect(math).toEqual(['\\\\[\n          /alpha\n      \\\\]']);
     });
   });
 
@@ -92,7 +91,7 @@ describe('jupyter-ui', () => {
     it('should recombine text split with removeMath', () => {
       const input = 'hello, $ /alpha $, there';
       const { text, math } = removeMath(input);
-      expect(replaceMath(text, math)).to.equal(input);
+      expect(replaceMath(text, math)).toBe(input);
     });
   });
 });

+ 6 - 7
tests/test-rendermime/src/mimemodel.spec.ts → packages/rendermime/test/mimemodel.spec.ts

@@ -1,16 +1,15 @@
 // Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
 
-import { expect } from 'chai';
+import 'jest';
 
-import { MimeModel } from '@jupyterlab/rendermime';
+import { MimeModel } from '../src';
 
 describe('rendermime/mimemodel', () => {
   describe('MimeModel', () => {
     describe('#constructor()', () => {
       it('should create a new mime model', () => {
         const model = new MimeModel();
-        expect(model).to.be.an.instanceof(MimeModel);
+        expect(model).toBeInstanceOf(MimeModel);
       });
 
       it('should accept arguments', () => {
@@ -18,7 +17,7 @@ describe('rendermime/mimemodel', () => {
           data: { foo: 1 },
           metadata: { bar: 'baz' }
         });
-        expect(model).to.be.an.instanceof(MimeModel);
+        expect(model).toBeInstanceOf(MimeModel);
       });
     });
 
@@ -27,7 +26,7 @@ describe('rendermime/mimemodel', () => {
         const model = new MimeModel({
           data: { bar: 'baz' }
         });
-        expect(model.data['bar']).to.equal('baz');
+        expect(model.data['bar']).toBe('baz');
       });
     });
 
@@ -36,7 +35,7 @@ describe('rendermime/mimemodel', () => {
         const model = new MimeModel({
           metadata: { bar: 'baz' }
         });
-        expect(model.metadata['bar']).to.equal('baz');
+        expect(model.metadata['bar']).toBe('baz');
       });
     });
   });

+ 13 - 14
tests/test-rendermime/src/outputmodel.spec.ts → packages/rendermime/test/outputmodel.spec.ts

@@ -1,11 +1,10 @@
 // Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
 
-import { expect } from 'chai';
+import 'jest';
 
 import * as nbformat from '@jupyterlab/nbformat';
 
-import { OutputModel } from '@jupyterlab/rendermime';
+import { OutputModel } from '../src';
 
 import { NBTestUtils } from '@jupyterlab/testutils';
 
@@ -31,37 +30,37 @@ describe('rendermime/outputmodel', () => {
       it('should create a new output model', () => {
         const value = DEFAULT_EXECUTE;
         let model = new OutputModel({ value });
-        expect(model).to.be.an.instanceof(OutputModel);
+        expect(model).toBeInstanceOf(OutputModel);
         model = new OutputModel({ value, trusted: true });
-        expect(model).to.be.an.instanceof(OutputModel);
+        expect(model).toBeInstanceOf(OutputModel);
       });
     });
 
     describe('#type', () => {
       it('should be the output type', () => {
         let model = new OutputModel({ value: DEFAULT_EXECUTE });
-        expect(model.type).to.equal(DEFAULT_EXECUTE.output_type);
+        expect(model.type).toBe(DEFAULT_EXECUTE.output_type);
         model = new OutputModel({ value: DEFAULT_STREAM });
-        expect(model.type).to.equal(DEFAULT_STREAM.output_type);
+        expect(model.type).toBe(DEFAULT_STREAM.output_type);
       });
     });
 
     describe('#executionCount', () => {
       it('should be the execution count of an execution result', () => {
         const model = new OutputModel({ value: DEFAULT_EXECUTE });
-        expect(model.executionCount).to.equal(1);
+        expect(model.executionCount).toBe(1);
       });
 
       it('should be null for non-execution results', () => {
         const model = new OutputModel({ value: DEFAULT_STREAM });
-        expect(model.executionCount).to.be.null;
+        expect(model.executionCount).toBeNull();
       });
     });
 
     describe('#toJSON()', () => {
       it('should yield the raw value', () => {
         const model = new OutputModel({ value: DEFAULT_EXECUTE });
-        expect(model.toJSON()).to.deep.equal(DEFAULT_EXECUTE);
+        expect(model.toJSON()).toEqual(DEFAULT_EXECUTE);
       });
     });
 
@@ -70,7 +69,7 @@ describe('rendermime/outputmodel', () => {
         for (let i = 0; i < NBTestUtils.DEFAULT_OUTPUTS.length; i++) {
           const output = NBTestUtils.DEFAULT_OUTPUTS[i];
           const bundle = OutputModel.getData(output);
-          expect(Object.keys(bundle).length).to.not.equal(0);
+          expect(Object.keys(bundle).length).not.toBe(0);
         }
       });
     });
@@ -78,13 +77,13 @@ describe('rendermime/outputmodel', () => {
     describe('.getMetadata()', () => {
       it('should get the metadata from the bundle', () => {
         const metadata = OutputModel.getMetadata(DEFAULT_EXECUTE);
-        expect(metadata['foo']).to.equal(1);
-        expect(metadata['bar']).to.equal('baz');
+        expect(metadata['foo']).toBe(1);
+        expect(metadata['bar']).toBe('baz');
       });
 
       it('should handle output with no metadata field', () => {
         const metadata = OutputModel.getMetadata(DEFAULT_STREAM);
-        expect(Object.keys(metadata).length).to.equal(0);
+        expect(Object.keys(metadata).length).toBe(0);
       });
     });
   });

+ 70 - 82
tests/test-rendermime/src/registry.spec.ts → packages/rendermime/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, JSONObject } from '@lumino/coreutils';
 
@@ -22,12 +21,11 @@ import {
   IRenderMime,
   RenderedText,
   RenderMimeRegistry
-} from '@jupyterlab/rendermime';
+} from '../src';
 
-import {
-  defaultRenderMime,
-  createFileContextWithKernel
-} from '@jupyterlab/testutils';
+import { defaultRenderMime } from '@jupyterlab/testutils';
+
+import * as Mock from '@jupyterlab/testutils/lib/mock';
 
 function createModel(data: JSONObject): IRenderMime.IMimeModel {
   return new MimeModel({ data });
@@ -44,8 +42,8 @@ describe('rendermime/registry', () => {
   let r: RenderMimeRegistry;
   let RESOLVER: IRenderMime.IResolver;
 
-  before(async () => {
-    const fileContext = await createFileContextWithKernel();
+  beforeAll(async () => {
+    const fileContext = Mock.createFileContext(true);
     await fileContext.initialize(true);
 
     // The context initialization kicks off a sessionContext initialization,
@@ -62,34 +60,34 @@ describe('rendermime/registry', () => {
   describe('RenderMimeRegistry', () => {
     describe('#constructor()', () => {
       it('should create a new rendermime instance', () => {
-        expect(r instanceof RenderMimeRegistry).to.equal(true);
+        expect(r instanceof RenderMimeRegistry).toBe(true);
       });
     });
 
     describe('#resolver', () => {
       it('should be the resolver used by the rendermime', () => {
-        expect(r.resolver).to.be.null;
+        expect(r.resolver).toBeNull();
         const clone = r.clone({ resolver: RESOLVER });
-        expect(clone.resolver).to.equal(RESOLVER);
+        expect(clone.resolver).toBe(RESOLVER);
       });
     });
 
     describe('#linkHandler', () => {
       it('should be the link handler used by the rendermime', () => {
-        expect(r.linkHandler).to.be.null;
+        expect(r.linkHandler).toBeNull();
         const handler = {
           handleLink: () => {
             /* no-op */
           }
         };
         const clone = r.clone({ linkHandler: handler });
-        expect(clone.linkHandler).to.equal(handler);
+        expect(clone.linkHandler).toBe(handler);
       });
     });
 
     describe('#latexTypesetter', () => {
       it('should be the null typesetter by default', () => {
-        expect(r.latexTypesetter).to.be.null;
+        expect(r.latexTypesetter).toBeNull();
       });
 
       it('should be clonable', () => {
@@ -99,23 +97,23 @@ describe('rendermime/registry', () => {
         };
         const typesetter1 = new MathJaxTypesetter(args);
         const clone1 = r.clone({ latexTypesetter: typesetter1 });
-        expect(clone1.latexTypesetter).to.equal(typesetter1);
+        expect(clone1.latexTypesetter).toBe(typesetter1);
         const typesetter2 = new MathJaxTypesetter(args);
         const clone2 = r.clone({ latexTypesetter: typesetter2 });
-        expect(clone2.latexTypesetter).to.equal(typesetter2);
+        expect(clone2.latexTypesetter).toBe(typesetter2);
       });
     });
 
     describe('#createRenderer()', () => {
       it('should create a mime renderer', () => {
         const w = r.createRenderer('text/plain');
-        expect(w instanceof Widget).to.equal(true);
+        expect(w instanceof Widget).toBe(true);
       });
 
       it('should raise an error for an unregistered mime type', () => {
         expect(() => {
           r.createRenderer('text/fizz');
-        }).to.throw();
+        }).toThrowError();
       });
 
       it('should render json data', async () => {
@@ -124,7 +122,7 @@ describe('rendermime/registry', () => {
         });
         const w = r.createRenderer('application/json');
         await w.renderModel(model);
-        expect(w.node.textContent).to.equal('{\n  "foo": 1\n}');
+        expect(w.node.textContent).toBe('{\n  "foo": 1\n}');
       });
 
       it('should send a url resolver', async () => {
@@ -140,16 +138,16 @@ describe('rendermime/registry', () => {
               return Promise.resolve(url);
             },
             getDownloadUrl: (url: string) => {
-              expect(called0).to.equal(true);
+              expect(called0).toBe(true);
               called1 = true;
-              expect(url).to.equal('./foo%2520');
+              expect(url).toBe('./foo%2520');
               return Promise.resolve(url);
             }
           }
         });
         const w = r.createRenderer('text/html');
         await w.renderModel(model);
-        expect(called1).to.equal(true);
+        expect(called1).toBe(true);
       });
 
       it('should send a link handler', async () => {
@@ -161,14 +159,14 @@ describe('rendermime/registry', () => {
           resolver: RESOLVER,
           linkHandler: {
             handleLink: (node: HTMLElement, url: string) => {
-              expect(url).to.equal('foo/bar.txt');
+              expect(url).toBe('foo/bar.txt');
               called = true;
             }
           }
         });
         const w = r.createRenderer('text/html');
         await w.renderModel(model);
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
       });
 
       it('should send decoded paths to link handler', async () => {
@@ -180,14 +178,14 @@ describe('rendermime/registry', () => {
           resolver: RESOLVER,
           linkHandler: {
             handleLink: (node: HTMLElement, path: string) => {
-              expect(path).to.equal('foo%20/bår.txt');
+              expect(path).toBe('foo%20/bår.txt');
               called = true;
             }
           }
         });
         const w = r.createRenderer('text/html');
         await w.renderModel(model);
-        expect(called).to.equal(true);
+        expect(called).toBe(true);
       });
     });
 
@@ -197,12 +195,12 @@ describe('rendermime/registry', () => {
           'text/plain': 'foo',
           'text/html': '<h1>foo</h1>'
         });
-        expect(r.preferredMimeType(model.data, 'any')).to.equal('text/html');
+        expect(r.preferredMimeType(model.data, 'any')).toBe('text/html');
       });
 
       it('should return `undefined` if there are no registered mimeTypes', () => {
         const model = createModel({ 'text/fizz': 'buzz' });
-        expect(r.preferredMimeType(model.data, 'any')).to.be.undefined;
+        expect(r.preferredMimeType(model.data, 'any')).toBeUndefined();
       });
 
       it('should select the mimeType that is safe', () => {
@@ -212,7 +210,7 @@ describe('rendermime/registry', () => {
           'image/png':
             'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
         });
-        expect(r.preferredMimeType(model.data)).to.equal('image/png');
+        expect(r.preferredMimeType(model.data)).toBe('image/png');
       });
 
       it('should render the mimeType that is sanitizable', () => {
@@ -220,21 +218,21 @@ describe('rendermime/registry', () => {
           'text/plain': 'foo',
           'text/html': '<h1>foo</h1>'
         });
-        expect(r.preferredMimeType(model.data)).to.equal('text/html');
+        expect(r.preferredMimeType(model.data)).toBe('text/html');
       });
 
       it('should return `undefined` if only unsafe options with default `ensure`', () => {
         const model = createModel({
           'image/svg+xml': ''
         });
-        expect(r.preferredMimeType(model.data)).to.be.undefined;
+        expect(r.preferredMimeType(model.data)).toBeUndefined();
       });
 
       it('should return `undefined` if only unsafe options with `ensure`', () => {
         const model = createModel({
           'image/svg+xml': ''
         });
-        expect(r.preferredMimeType(model.data, 'ensure')).to.be.undefined;
+        expect(r.preferredMimeType(model.data, 'ensure')).toBeUndefined();
       });
 
       it('should return safe option if called with `prefer`', () => {
@@ -242,27 +240,23 @@ describe('rendermime/registry', () => {
           'image/svg+xml': '',
           'text/plain': ''
         });
-        expect(r.preferredMimeType(model.data, 'prefer')).to.equal(
-          'text/plain'
-        );
+        expect(r.preferredMimeType(model.data, 'prefer')).toBe('text/plain');
       });
 
       it('should return unsafe option if called with `prefer`, and no safe alternative', () => {
         const model = createModel({
           'image/svg+xml': ''
         });
-        expect(r.preferredMimeType(model.data, 'prefer')).to.equal(
-          'image/svg+xml'
-        );
+        expect(r.preferredMimeType(model.data, 'prefer')).toBe('image/svg+xml');
       });
     });
 
     describe('#clone()', () => {
       it('should clone the rendermime instance with shallow copies of data', () => {
         const c = r.clone();
-        expect(toArray(c.mimeTypes)).to.deep.equal(r.mimeTypes);
+        expect(toArray(c.mimeTypes)).toEqual(r.mimeTypes);
         c.addFactory(fooFactory);
-        expect(r).to.not.equal(c);
+        expect(r).not.toBe(c);
       });
     });
 
@@ -270,15 +264,15 @@ describe('rendermime/registry', () => {
       it('should add a factory', () => {
         r.addFactory(fooFactory);
         const index = r.mimeTypes.indexOf('text/foo');
-        expect(index).to.equal(r.mimeTypes.length - 1);
+        expect(index).toBe(r.mimeTypes.length - 1);
       });
 
       it('should take an optional rank', () => {
         const len = r.mimeTypes.length;
         r.addFactory(fooFactory, 0);
         const index = r.mimeTypes.indexOf('text/foo');
-        expect(index).to.equal(0);
-        expect(r.mimeTypes.length).to.equal(len + 1);
+        expect(index).toBe(0);
+        expect(r.mimeTypes.length).toBe(len + 1);
       });
     });
 
@@ -286,7 +280,7 @@ describe('rendermime/registry', () => {
       it('should remove a factory by mimeType', () => {
         r.removeMimeType('text/html');
         const model = createModel({ 'text/html': '<h1>foo</h1>' });
-        expect(r.preferredMimeType(model.data, 'any')).to.be.undefined;
+        expect(r.preferredMimeType(model.data, 'any')).toBeUndefined();
       });
 
       it('should be a no-op if the mimeType is not registered', () => {
@@ -297,22 +291,22 @@ describe('rendermime/registry', () => {
     describe('#getFactory()', () => {
       it('should get a factory by mimeType', () => {
         const f = r.getFactory('text/plain')!;
-        expect(f.mimeTypes).to.contain('text/plain');
+        expect(f.mimeTypes).toEqual(expect.arrayContaining(['text/plain']));
       });
 
       it('should return undefined for missing mimeType', () => {
-        expect(r.getFactory('hello/world')).to.be.undefined;
+        expect(r.getFactory('hello/world')).toBeUndefined();
       });
     });
 
     describe('#mimeTypes', () => {
       it('should get the ordered list of mimeTypes', () => {
-        expect(r.mimeTypes.indexOf('text/html')).to.not.equal(-1);
+        expect(r.mimeTypes.indexOf('text/html')).not.toBe(-1);
       });
     });
 
     describe('.UrlResolver', () => {
-      let manager: ServiceManager;
+      let manager: ServiceManager.IManager;
       let resolverSession: RenderMimeRegistry.UrlResolver;
       let resolverPath: RenderMimeRegistry.UrlResolver;
       let contents: Contents.IManager;
@@ -321,8 +315,8 @@ describe('rendermime/registry', () => {
       const urlParent = encodeURI(pathParent);
       const path = pathParent + '/pr%25 ' + UUID.uuid4();
 
-      before(async () => {
-        manager = new ServiceManager({ standby: 'never' });
+      beforeAll(async () => {
+        manager = new Mock.ServiceManagerMock();
         const drive = new Drive({ name: 'extra' });
         contents = manager.contents;
         contents.addDrive(drive);
@@ -342,22 +336,20 @@ describe('rendermime/registry', () => {
         });
       });
 
-      after(() => {
+      afterAll(() => {
         return session.shutdown();
       });
 
-      context('#constructor', () => {
+      describe('#constructor', () => {
         it('should create a UrlResolver instance', () => {
-          expect(resolverSession).to.be.an.instanceof(
-            RenderMimeRegistry.UrlResolver
-          );
-          expect(resolverPath).to.be.an.instanceof(
+          expect(resolverSession).toBeInstanceOf(
             RenderMimeRegistry.UrlResolver
           );
+          expect(resolverPath).toBeInstanceOf(RenderMimeRegistry.UrlResolver);
         });
       });
 
-      context('.path', () => {
+      describe('.path', () => {
         it('should give precedence to the explicit path over the session', async () => {
           const resolver = new RenderMimeRegistry.UrlResolver({
             session: new SessionContext({
@@ -369,11 +361,11 @@ describe('rendermime/registry', () => {
             contents: manager.contents,
             path: '/some/path/file.txt'
           });
-          expect(await resolver.resolveUrl('./foo')).to.equal('some/path/foo');
+          expect(await resolver.resolveUrl('./foo')).toBe('some/path/foo');
         });
 
         it('should fall back to the session path if only the session is given', () => {
-          expect(resolverSession.path).to.equal(path);
+          expect(resolverSession.path).toBe(path);
         });
 
         it('should allow the path to be changed', async () => {
@@ -387,24 +379,22 @@ describe('rendermime/registry', () => {
             contents: manager.contents
           });
           resolver.path = '/some/path/file.txt';
-          expect(await resolver.resolveUrl('./foo')).to.equal('some/path/foo');
+          expect(await resolver.resolveUrl('./foo')).toBe('some/path/foo');
           const resolver2 = new RenderMimeRegistry.UrlResolver({
             path: '/some/path/file.txt',
             contents: manager.contents
           });
           resolver2.path = '/other/path/file.txt';
-          expect(await resolver2.resolveUrl('./foo')).to.equal(
-            'other/path/foo'
-          );
+          expect(await resolver2.resolveUrl('./foo')).toBe('other/path/foo');
         });
       });
 
-      context('#resolveUrl()', () => {
+      describe('#resolveUrl()', () => {
         it('should resolve a relative url', async () => {
-          expect(await resolverSession.resolveUrl('./foo')).to.equal(
+          expect(await resolverSession.resolveUrl('./foo')).toBe(
             urlParent + '/foo'
           );
-          expect(await resolverPath.resolveUrl('./foo')).to.equal(
+          expect(await resolverPath.resolveUrl('./foo')).toBe(
             urlParent + '/foo'
           );
         });
@@ -420,62 +410,60 @@ describe('rendermime/registry', () => {
             contents: manager.contents
           });
           const path = await resolver.resolveUrl('./foo');
-          expect(path).to.equal(urlParent + '/foo');
+          expect(path).toBe(urlParent + '/foo');
         });
 
         it('should ignore urls that have a protocol', async () => {
-          expect(await resolverSession.resolveUrl('http://foo')).to.equal(
+          expect(await resolverSession.resolveUrl('http://foo')).toBe(
             'http://foo'
           );
-          expect(await resolverPath.resolveUrl('http://foo')).to.equal(
+          expect(await resolverPath.resolveUrl('http://foo')).toBe(
             'http://foo'
           );
         });
 
         it('should resolve URLs with escapes', async () => {
-          expect(await resolverSession.resolveUrl('has%20space')).to.equal(
+          expect(await resolverSession.resolveUrl('has%20space')).toBe(
             urlParent + '/has%20space'
           );
-          expect(await resolverPath.resolveUrl('has%20space')).to.equal(
+          expect(await resolverPath.resolveUrl('has%20space')).toBe(
             urlParent + '/has%20space'
           );
         });
       });
 
-      context('#getDownloadUrl()', () => {
+      describe('#getDownloadUrl()', () => {
         it('should resolve an absolute server url to a download url', async () => {
           const contextPromise = resolverPath.getDownloadUrl('foo');
           const contentsPromise = contents.getDownloadUrl('foo');
           const values = await Promise.all([contextPromise, contentsPromise]);
-          expect(values[0]).to.equal(values[1]);
+          expect(values[0]).toBe(values[1]);
         });
 
         it('should resolve escapes correctly', async () => {
           const contextPromise = resolverPath.getDownloadUrl('foo%2520test');
           const contentsPromise = contents.getDownloadUrl('foo%20test');
           const values = await Promise.all([contextPromise, contentsPromise]);
-          expect(values[0]).to.equal(values[1]);
+          expect(values[0]).toBe(values[1]);
         });
 
         it('should ignore urls that have a protocol', async () => {
           const path = await resolverPath.getDownloadUrl('http://foo');
-          expect(path).to.equal('http://foo');
+          expect(path).toBe('http://foo');
         });
       });
 
-      context('#isLocal', () => {
+      describe('#isLocal', () => {
         it('should return true for a registered IDrive`', () => {
-          expect(resolverPath.isLocal('extra:path/to/file')).to.equal(true);
+          expect(resolverPath.isLocal('extra:path/to/file')).toBe(true);
         });
 
         it('should return false for an unrecognized Drive`', () => {
-          expect(resolverPath.isLocal('unregistered:path/to/file')).to.equal(
-            false
-          );
+          expect(resolverPath.isLocal('unregistered:path/to/file')).toBe(false);
         });
 
         it('should return true for a normal filesystem-like path`', () => {
-          expect(resolverPath.isLocal('path/to/file')).to.equal(true);
+          expect(resolverPath.isLocal('path/to/file')).toBe(true);
         });
       });
     });

+ 30 - 0
packages/rendermime/tsconfig.test.json

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

+ 0 - 1
tests/test-rendermime/karma-cov.conf.js

@@ -1 +0,0 @@
-module.exports = require('../karma-cov.conf');

+ 0 - 1
tests/test-rendermime/karma.conf.js

@@ -1 +0,0 @@
-module.exports = require('../karma.conf');

+ 0 - 41
tests/test-rendermime/package.json

@@ -1,41 +0,0 @@
-{
-  "name": "@jupyterlab/test-rendermime",
-  "version": "2.1.0",
-  "private": true,
-  "scripts": {
-    "build": "tsc -b",
-    "clean": "rimraf build && rimraf coverage",
-    "coverage": "python run-test.py --browsers=ChromeHeadless karma-cov.conf.js",
-    "test": "jlpm run test:firefox-headless",
-    "test:chrome": "python run-test.py --browsers=Chrome karma.conf.js",
-    "test:chrome-headless": "python run-test.py --browsers=ChromeHeadless karma.conf.js",
-    "test:debug": "python run-test.py  --browsers=Chrome --singleRun=false --debug=true --browserNoActivityTimeout=10000000 karma.conf.js",
-    "test:firefox": "python run-test.py --browsers=Firefox karma.conf.js",
-    "test:firefox-headless": "python run-test.py --browsers=FirefoxHeadless karma.conf.js",
-    "test:ie": "python run-test.py  --browsers=IE karma.conf.js",
-    "watch": "tsc -b --watch",
-    "watch:src": "tsc -p src --watch"
-  },
-  "dependencies": {
-    "@jupyterlab/apputils": "^2.1.0",
-    "@jupyterlab/coreutils": "^4.1.0",
-    "@jupyterlab/mathjax2": "^2.1.0",
-    "@jupyterlab/nbformat": "^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/widgets": "^1.11.1",
-    "chai": "^4.2.0"
-  },
-  "devDependencies": {
-    "@types/chai": "^4.2.7",
-    "@types/mocha": "^7.0.2",
-    "karma": "^4.4.1",
-    "karma-chrome-launcher": "~3.1.0",
-    "puppeteer": "~2.0.0",
-    "rimraf": "~3.0.0",
-    "typescript": "~3.7.3"
-  }
-}

+ 0 - 10
tests/test-rendermime/run-test.py

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

+ 0 - 34
tests/test-rendermime/tsconfig.json

@@ -1,34 +0,0 @@
-{
-  "extends": "../../tsconfigbase",
-  "compilerOptions": {
-    "outDir": "build",
-    "types": ["mocha", "node"],
-    "composite": false,
-    "rootDir": "src",
-    "skipLibCheck": true
-  },
-  "include": ["src/*"],
-  "references": [
-    {
-      "path": "../../packages/apputils"
-    },
-    {
-      "path": "../../packages/coreutils"
-    },
-    {
-      "path": "../../packages/mathjax2"
-    },
-    {
-      "path": "../../packages/nbformat"
-    },
-    {
-      "path": "../../packages/rendermime"
-    },
-    {
-      "path": "../../packages/services"
-    },
-    {
-      "path": "../../testutils"
-    }
-  ]
-}

+ 22 - 0
testutils/src/mock.ts

@@ -5,6 +5,8 @@ import 'jest';
 
 import { ISessionContext, SessionContext } from '@jupyterlab/apputils';
 
+import { Context, TextModelFactory } from '@jupyterlab/docregistry';
+
 import {
   Kernel,
   KernelMessage,
@@ -615,6 +617,26 @@ export const MockShellFuture = jest.fn<Kernel.IShellFuture, []>(() => {
   return thisObject;
 });
 
+/**
+ * Create a context for a file.
+ */
+export function createFileContext(startKernel = false): Context {
+  const path = UUID.uuid4() + '.txt';
+  const manager = new ServiceManagerMock();
+  const factory = new TextModelFactory();
+
+  return new Context({
+    manager,
+    factory,
+    path,
+    kernelPreference: {
+      shouldStart: startKernel,
+      canStart: startKernel,
+      autoStartDefault: startKernel
+    }
+  });
+}
+
 /**
  * A namespace for module private data.
  */