diff --git a/packages/dev-server-hmr/package.json b/packages/dev-server-hmr/package.json index a2445e6bc5..106962d5c6 100644 --- a/packages/dev-server-hmr/package.json +++ b/packages/dev-server-hmr/package.json @@ -28,8 +28,8 @@ "build": "tsc", "start:lit-html": "wds --config demo/lit-html/server.config.mjs", "start:vanilla": "wds --config demo/vanilla/server.config.mjs", - "test:node": "mocha \"test/**/*.test.ts\" --require ts-node/register --reporter dot", - "test:watch": "mocha \"test/**/*.test.ts\" --require ts-node/register --watch --watch-files src,test" + "test:node": "node --experimental-strip-types --test --test-force-exit test/**/*.test.ts", + "test:watch": "node --experimental-strip-types --test --test-force-exit --watch test/**/*.test.ts" }, "files": [ "*.d.ts", diff --git a/packages/dev-server-hmr/test/HmrPlugin.test.ts b/packages/dev-server-hmr/test/HmrPlugin.test.ts index dd5e3c3763..171f91ec6f 100644 --- a/packages/dev-server-hmr/test/HmrPlugin.test.ts +++ b/packages/dev-server-hmr/test/HmrPlugin.test.ts @@ -1,20 +1,20 @@ -import { expect } from 'chai'; -import { stubMethod, restore as restoreStubs } from 'hanbi'; +import { describe, it, afterEach, mock } from 'node:test'; +import assert from 'node:assert/strict'; import { createTestServer, fetchText, expectIncludes } from '@web/dev-server-core/test-helpers'; import { posix as pathUtil } from 'path'; -import { hmrPlugin } from '../src/index.js'; -import { NAME_HMR_CLIENT_IMPORT } from '../src/HmrPlugin.js'; -import { mockFile, mockFiles } from './utils.js'; +import { hmrPlugin } from '../dist/index.js'; +import { NAME_HMR_CLIENT_IMPORT } from '../dist/HmrPlugin.js'; +import { mockFile, mockFiles } from './utils.ts'; describe('HmrPlugin', () => { afterEach(async () => { - restoreStubs(); + mock.restoreAll(); }); it('should emit update for tracked files', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile( '/foo.js', @@ -26,12 +26,13 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/foo.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/foo.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/foo.js')); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/foo.js', @@ -44,7 +45,7 @@ describe('HmrPlugin', () => { it('should bubble updates for changed dependencies', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile( '/foo.js', @@ -55,13 +56,14 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/foo.js`); await fetch(`${host}/bar.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/bar.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/bar.js')); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/foo.js', @@ -74,7 +76,7 @@ describe('HmrPlugin', () => { it('should not reload if dependent handles change', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile('/foo.js', `import '/bar.js'; import.meta.hot.accept();`), mockFile('/bar.js', `export const s = 808;`), @@ -82,14 +84,15 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/foo.js`); await fetch(`${host}/bar.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/bar.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/bar.js')); - expect(stub.callCount).to.equal(1); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal(stub.mock.callCount(), 1); + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/foo.js', @@ -102,7 +105,7 @@ describe('HmrPlugin', () => { it('should reload if dependents do not handle change', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile('/foo.js', `import '/bar.js';`), mockFile('/bar.js', `export const s = 808;`), @@ -110,14 +113,15 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/foo.js`); await fetch(`${host}/bar.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/bar.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/bar.js')); - expect(stub.callCount).to.equal(1); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal(stub.mock.callCount(), 1); + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:reload', }), @@ -129,7 +133,7 @@ describe('HmrPlugin', () => { it('handles dependencies referenced relatively', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile( '/root/foo.js', @@ -140,13 +144,14 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/root/foo.js`); await fetch(`${host}/root/bar.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/root/bar.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/root/bar.js')); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/root/foo.js', @@ -159,7 +164,7 @@ describe('HmrPlugin', () => { it('should bubble updates for changed dynamic import dependencies', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile( '/foo.js', @@ -170,13 +175,14 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/foo.js`); await fetch(`${host}/bar.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/bar.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/bar.js')); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/foo.js', @@ -189,7 +195,7 @@ describe('HmrPlugin', () => { it('imports changed dependencies with a unique URL', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFiles({ '/a.js': "import '/b.js'; import '/c.js'; import.meta.hot.accept();", @@ -204,11 +210,11 @@ describe('HmrPlugin', () => { await fetchText(`${host}/a.js`); await fetchText(`${host}/b.js`); await fetchText(`${host}/c.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/b.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/b.js')); const updatedA = await fetchText(`${host}/a.js?m=1234567890123`); await fetchText(`${host}/b.js?m=1234567890123`); - expect(/import '\/b\.js\?m=\d{13}';/.test(updatedA)).to.equal(true); + assert.match(updatedA, /import '\/b\.js\?m=\d{13}';/); expectIncludes(updatedA, "import '/c.js';"); } finally { await server.stop(); @@ -217,7 +223,7 @@ describe('HmrPlugin', () => { it('imports deeply changed dependencies with a unique URL', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFiles({ '/a.js': "import '/b.js'; import.meta.hot.accept();", @@ -232,13 +238,13 @@ describe('HmrPlugin', () => { await fetchText(`${host}/a.js`); await fetchText(`${host}/b.js`); await fetchText(`${host}/c.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/c.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/c.js')); const updatedA = await fetchText(`${host}/a.js?m=1234567890123`); const updatedB = await fetchText(`${host}/b.js?m=1234567890123`); await fetchText(`${host}/c.js?m=1234567890123`); - expect(/import '\/b\.js\?m=\d{13}';/.test(updatedA)).to.equal(true); - expect(/import '\/c\.js\?m=\d{13}';/.test(updatedB)).to.equal(true); + assert.match(updatedA, /import '\/b\.js\?m=\d{13}';/); + assert.match(updatedB, /import '\/c\.js\?m=\d{13}';/); } finally { await server.stop(); } @@ -246,7 +252,7 @@ describe('HmrPlugin', () => { it('multiple dependents will import deep dependency changes with a unique URL', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFiles({ '/a1.js': "import '/b.js'; import.meta.hot.accept(); // a1", @@ -264,14 +270,14 @@ describe('HmrPlugin', () => { await fetchText(`${host}/a2.js`); await fetchText(`${host}/b.js`); await fetchText(`${host}/c.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/c.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/c.js')); const updatedA1 = await fetchText(`${host}/a1.js?m=1234567890123`); const updatedA2 = await fetchText(`${host}/a2.js?m=1234567890123`); await fetchText(`${host}/b.js?m=1234567890123`); await fetchText(`${host}/c.js?m=1234567890123`); - expect(/import '\/b\.js\?m=\d{13}';/.test(updatedA1)).to.equal(true); - expect(/import '\/b\.js\?m=\d{13}';/.test(updatedA2)).to.equal(true); + assert.match(updatedA1, /import '\/b\.js\?m=\d{13}';/); + assert.match(updatedA2, /import '\/b\.js\?m=\d{13}';/); } finally { await server.stop(); } @@ -279,7 +285,7 @@ describe('HmrPlugin', () => { it('does not get confused by dynamic imports with non string literals', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile( '/foo.js', @@ -290,13 +296,14 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/foo.js`); await fetch(`${host}/bar.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/bar.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/bar.js')); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/foo.js', @@ -309,7 +316,7 @@ describe('HmrPlugin', () => { it('should emit reload for tracked files', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile( '/foo.js', @@ -321,12 +328,13 @@ describe('HmrPlugin', () => { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); try { await fetch(`${host}/foo.js`); - fileWatcher.emit('change', pathUtil.join(__dirname, '/foo.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/foo.js')); - expect(stub.firstCall!.args[0]).to.equal( + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:reload', }), @@ -338,14 +346,14 @@ describe('HmrPlugin', () => { it('serves a hmr client', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [hmrPlugin()], }); try { const response = await fetch(`${host}${NAME_HMR_CLIENT_IMPORT}`); const body = await response.text(); - expect(body.includes('class HotModule')).to.equal(true); + assert.ok(body.includes('class HotModule')); } finally { await server.stop(); } @@ -353,7 +361,7 @@ describe('HmrPlugin', () => { it('transforms hmr-capable js files', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFile( '/foo.js', @@ -369,7 +377,7 @@ describe('HmrPlugin', () => { const response = await fetch(`${host}/foo.js`); const body = await response.text(); - expect(body.includes('__WDS_HMR__')).to.equal(true); + assert.ok(body.includes('__WDS_HMR__')); } finally { await server.stop(); } @@ -377,7 +385,7 @@ describe('HmrPlugin', () => { it('does not transform non-hmr js files', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [mockFile('/foo.js', `export const foo = 5;`), hmrPlugin()], }); @@ -385,7 +393,7 @@ describe('HmrPlugin', () => { const response = await fetch(`${host}/foo.js`); const body = await response.text(); - expect(body.includes('__WDS_HMR__')).to.equal(false); + assert.ok(!body.includes('__WDS_HMR__')); } finally { await server.stop(); } diff --git a/packages/dev-server-hmr/test/browser.test.ts b/packages/dev-server-hmr/test/browser.test.ts index 1f3b62ca33..9bf66a5ca8 100644 --- a/packages/dev-server-hmr/test/browser.test.ts +++ b/packages/dev-server-hmr/test/browser.test.ts @@ -1,11 +1,12 @@ -import { expect } from 'chai'; -import { stubMethod } from 'hanbi'; +import { describe, it, before, after, mock } from 'node:test'; +import assert from 'node:assert/strict'; import { createTestServer, expectIncludes } from '@web/dev-server-core/test-helpers'; -import { Browser, HTTPResponse, launch as launchPuppeteer, Page } from 'puppeteer'; +import puppeteer from 'puppeteer'; +import type { Browser, HTTPResponse, Page } from 'puppeteer'; import { posix as pathUtil } from 'path'; -import { hmrPlugin } from '../src/index.js'; -import { mockFiles } from './utils.js'; +import { hmrPlugin } from '../dist/index.js'; +import { mockFiles } from './utils.ts'; function trackErrors(page: Page) { const errors: any[] = []; @@ -36,12 +37,11 @@ async function mockFaviconRequests(page: Page) { }); } -describe('browser tests', function () { - this.timeout(5000); +describe('browser tests', { timeout: 5000 }, () => { let browser: Browser; before(async () => { - browser = await launchPuppeteer(); + browser = await puppeteer.launch(); }); after(async () => { @@ -50,7 +50,7 @@ describe('browser tests', function () { it('should bubble when bubbles is true', async function () { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [ mockFiles({ '/foo.html': '', @@ -61,20 +61,22 @@ describe('browser tests', function () { ], }); const { fileWatcher, webSockets } = server; - const stub = stubMethod(webSockets!, 'send'); + const stub = mock.method(webSockets!, 'send'); const page = await browser.newPage(); try { await page.goto(`${host}/foo.html`, { waitUntil: 'networkidle0' }); - fileWatcher.emit('change', pathUtil.join(__dirname, '/bar.js')); + fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/bar.js')); - expect(stub.callCount).to.equal(2); - expect(stub.getCall(0)!.args[0]).to.equal( + assert.equal(stub.mock.callCount(), 2); + assert.equal( + stub.mock.calls[0].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/bar.js', }), ); - expect(stub.getCall(1)!.args[0]).to.equal( + assert.equal( + stub.mock.calls[1].arguments[0], JSON.stringify({ type: 'hmr:update', url: '/foo.js', @@ -93,7 +95,7 @@ describe('browser tests', function () { 'import.meta.hot.accept(); document.body.appendChild(document.createTextNode(" a "));', }; const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [mockFiles(files), hmrPlugin()], }); const page = await browser.newPage(); @@ -105,7 +107,7 @@ describe('browser tests', function () { expectIncludes(await page.content(), ' a '); files['/foo.js'] = files['/foo.js'].replace('" a "', '" b "'); - server.fileWatcher.emit('change', pathUtil.join(__dirname, '/foo.js')); + server.fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/foo.js')); await page.waitForResponse((r: HTTPResponse) => r.url().startsWith(`${host}/foo.js`)); expectIncludes(await page.content(), ' a b '); @@ -126,7 +128,7 @@ describe('browser tests', function () { '/bar.js': 'export default " a ";', }; const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [mockFiles(files), hmrPlugin()], }); const page = await browser.newPage(); @@ -138,7 +140,7 @@ describe('browser tests', function () { expectIncludes(await page.content(), ' a '); files['/bar.js'] = 'export default " b ";'; - server.fileWatcher.emit('change', pathUtil.join(__dirname, '/bar.js')); + server.fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/bar.js')); await page.waitForResponse((r: HTTPResponse) => r.url().startsWith(`${host}/bar.js`)); expectIncludes(await page.content(), ' a b '); @@ -164,7 +166,7 @@ describe('browser tests', function () { '/baz.js': 'export default " a ";', }; const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [mockFiles(files), hmrPlugin()], }); const page = await browser.newPage(); @@ -175,7 +177,7 @@ describe('browser tests', function () { expectIncludes(await page.content(), ' foo a bar a '); files['/baz.js'] = 'export default " b ";'; - server.fileWatcher.emit('change', pathUtil.join(__dirname, '/baz.js')); + server.fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/baz.js')); await Promise.all([ page.waitForResponse((r: HTTPResponse) => r.url().startsWith(`${host}/foo.js`)), page.waitForResponse((r: HTTPResponse) => r.url().startsWith(`${host}/bar.js`)), @@ -205,7 +207,7 @@ describe('browser tests', function () { '/baz.js': 'document.body.appendChild(document.createTextNode(" b "));', }; const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, plugins: [mockFiles(files), hmrPlugin()], }); const page = await browser.newPage(); @@ -217,7 +219,7 @@ describe('browser tests', function () { await page.evaluate('document.body.appendChild(document.createTextNode(" c "))'); expectIncludes(await page.content(), ' a b c '); - server.fileWatcher.emit('change', pathUtil.join(__dirname, '/baz.js')); + server.fileWatcher.emit('change', pathUtil.join(import.meta.dirname, '/baz.js')); await page.waitForNavigation(); expectIncludes(await page.content(), ' a b '); diff --git a/packages/dev-server-hmr/test/utils.ts b/packages/dev-server-hmr/test/utils.ts index a79c7400f1..fb7494e706 100644 --- a/packages/dev-server-hmr/test/utils.ts +++ b/packages/dev-server-hmr/test/utils.ts @@ -1,4 +1,4 @@ -import { Context } from 'koa'; +import type { Context } from 'koa'; export const mockFile = (path: string, source: string) => ({ name: `test-file:${path}`,