From 2c7a50a4747da75f6bcb76a152d1c4f277ddb21c Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Tue, 2 Jun 2026 23:22:29 +0300 Subject: [PATCH 1/3] test(dev-server-core): migrate from mocha/chai to node:test Replace mocha/chai/hanbi with node:test and node:assert/strict across all 14 test files (97 tests), test helpers, and the published `src/test-helpers.ts` utility. Key changes: - chai `expect` assertions to node:assert/strict - hanbi `stubMethod` to `mock.method` from node:test - `__dirname` to `import.meta.dirname` - Source imports (`../src/`) to dist imports (`../dist/`) - Test-local imports use `.ts` extension for strip-types compat - `context()` to `describe()` (mocha alias not in node:test) - mocha test script to `node --experimental-strip-types --test` Note: `src/test-helpers.ts` is a published utility exported via `exports["./test-helpers"]`. Its `fetchText` function now uses `node:assert` instead of `chai` for the status check -- pending team decision on whether this is acceptable. Assisted-By: Claude Opus 4.6 (1M context) --- packages/dev-server-core/package.json | 5 +- packages/dev-server-core/src/test-helpers.ts | 4 +- packages/dev-server-core/test/helpers.ts | 6 +- .../middleware/basePathMiddleware.test.ts | 25 +++-- .../middleware/etagCacheMiddleware.test.ts | 33 +++--- .../historyApiFallbackMiddleware.test.ts | 53 ++++----- .../pluginFileParsedMiddleware.test.ts | 17 +-- .../pluginMimeTypeMiddleware.test.ts | 13 ++- .../middleware/pluginServeMiddleware.test.ts | 36 +++--- .../pluginTransformMiddleware.test.ts | 50 ++++----- .../middleware/serveFilesMiddleware.test.ts | 11 +- .../test/plugins/mimeTypesPlugin.test.ts | 22 ++-- .../test/plugins/parseDynamicImport.test.ts | 35 +++--- .../transformModuleImportsPlugin.test.ts | 104 ++++++++--------- .../test/server/DevServer.test.ts | 105 ++++++++---------- .../web-sockets/WebSocketsManager.test.ts | 21 ++-- .../test/web-sockets/webSocketsPlugin.test.ts | 27 ++--- 17 files changed, 284 insertions(+), 283 deletions(-) diff --git a/packages/dev-server-core/package.json b/packages/dev-server-core/package.json index 633e1c8378..fd13e0bfb7 100644 --- a/packages/dev-server-core/package.json +++ b/packages/dev-server-core/package.json @@ -39,8 +39,9 @@ "start:event-stream": "node demo/event-stream/start-server.js", "start:http2": "node demo/http2/start-server.js", "start:import-asset": "node demo/import-asset/start-server.js", - "test": "mocha \"test/**/*.test.{ts,js,mjs,cjs}\" --require ts-node/register --exit --reporter dot", - "test:watch": "mocha \"test/**/*.test.{ts,js,mjs,cjs}\" --require ts-node/register --watch --watch-files src,test" + "test": "node --experimental-strip-types --test --test-force-exit \"test/**/*.test.ts\"", + "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": [ ".self-signed-dev-server-ssl.cert", diff --git a/packages/dev-server-core/src/test-helpers.ts b/packages/dev-server-core/src/test-helpers.ts index 74e19db790..4bd8b8967b 100644 --- a/packages/dev-server-core/src/test-helpers.ts +++ b/packages/dev-server-core/src/test-helpers.ts @@ -1,5 +1,5 @@ import portfinder from 'portfinder'; -import { expect } from 'chai'; +import assert from 'node:assert/strict'; import { green, red, yellow } from 'nanocolors'; import { DevServer } from './server/DevServer.js'; @@ -63,7 +63,7 @@ export const timeout = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms export async function fetchText(url: string, init?: RequestInit) { const response = await fetch(url, init); - expect(response.status).to.equal(200); + assert.equal(response.status, 200); return response.text(); } diff --git a/packages/dev-server-core/test/helpers.ts b/packages/dev-server-core/test/helpers.ts index 398a0f9b2b..d99a33e866 100644 --- a/packages/dev-server-core/test/helpers.ts +++ b/packages/dev-server-core/test/helpers.ts @@ -5,12 +5,12 @@ import { fetchText, expectIncludes, virtualFilesPlugin, -} from '../src/test-helpers.js'; -import { DevServerCoreConfig } from '../src/server/DevServerCoreConfig.js'; +} from '../dist/test-helpers.js'; +import type { DevServerCoreConfig } from '../dist/server/DevServerCoreConfig.js'; export function createTestServer(config: Partial = {}) { return originalCreateTestServer({ - rootDir: path.resolve(__dirname, 'fixtures', 'basic'), + rootDir: path.resolve(import.meta.dirname, 'fixtures', 'basic'), ...config, }); } diff --git a/packages/dev-server-core/test/middleware/basePathMiddleware.test.ts b/packages/dev-server-core/test/middleware/basePathMiddleware.test.ts index 4de47b14ed..ef2f56519b 100644 --- a/packages/dev-server-core/test/middleware/basePathMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/basePathMiddleware.test.ts @@ -1,7 +1,8 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it, beforeEach, afterEach } from 'node:test'; -import { DevServer } from '../../src/server/DevServer.js'; -import { createTestServer } from '../helpers.js'; +import type { DevServer } from '../../dist/server/DevServer.js'; +import { createTestServer } from '../helpers.ts'; describe('base path middleware', () => { describe('without a trailing /', () => { @@ -19,20 +20,20 @@ describe('base path middleware', () => { const response = await fetch(`${host}/foo/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); it('can request without base path', async () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); }); - context('with a trailing /', () => { + describe('with a trailing /', () => { let host: string; let server: DevServer; beforeEach(async () => { @@ -47,16 +48,16 @@ describe('base path middleware', () => { const response = await fetch(`${host}/foo/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); it('can request without base path', async () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); }); }); diff --git a/packages/dev-server-core/test/middleware/etagCacheMiddleware.test.ts b/packages/dev-server-core/test/middleware/etagCacheMiddleware.test.ts index 75838be21a..d681b9f9e4 100644 --- a/packages/dev-server-core/test/middleware/etagCacheMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/etagCacheMiddleware.test.ts @@ -1,12 +1,13 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it, beforeEach, afterEach } from 'node:test'; import path from 'path'; import fs from 'fs'; import { nanoid } from 'nanoid'; -import { createTestServer, timeout } from '../helpers.js'; -import { DevServer } from '../../src/server/DevServer.js'; +import { createTestServer, timeout } from '../helpers.ts'; +import type { DevServer } from '../../dist/server/DevServer.js'; -const fixtureDir = path.resolve(__dirname, '..', 'fixtures', 'basic'); +const fixtureDir = path.resolve(import.meta.dirname, '..', 'fixtures', 'basic'); const testFileAName = '/cached-file-a.js'; const testFileBName = '/cached-file-b.js'; const testFileAPath = path.join(fixtureDir, testFileAName); @@ -24,7 +25,7 @@ describe('etag cache middleware', () => { server.stop(); }); - context('', () => { + describe('cached file a', () => { beforeEach(() => { fs.writeFileSync(testFileAPath, '// this file is cached', 'utf-8'); }); @@ -37,21 +38,21 @@ describe('etag cache middleware', () => { const initialResponse = await fetch(`${host}${testFileAName}`); const etag = initialResponse.headers.get('etag')!; - expect(initialResponse.status).to.equal(200); - expect(await initialResponse.text()).to.equal('// this file is cached'); + assert.equal(initialResponse.status, 200); + assert.equal(await initialResponse.text(), '// this file is cached'); - expect(etag).to.be.a('string'); + assert.equal(typeof etag, 'string'); const cachedResponse = await fetch(`${host}${testFileAName}`, { headers: { 'If-None-Match': etag, 'Cache-Control': 'max-age=3600' }, }); - expect(cachedResponse.status).to.equal(304); - expect(await cachedResponse.text()).to.equal(''); + assert.equal(cachedResponse.status, 304); + assert.equal(await cachedResponse.text(), ''); }); }); - context('', () => { + describe('cached file b', () => { beforeEach(() => { fs.writeFileSync(testFileBPath, '// this file is cached', 'utf-8'); }); @@ -66,9 +67,9 @@ describe('etag cache middleware', () => { const initialResponse = await fetch(`${host}${testFileBName}`); const etag = initialResponse.headers.get('etag'); - expect(initialResponse.status).to.equal(200); - expect(await initialResponse.text()).to.equal('// this file is cached'); - expect(etag).to.be.a('string'); + assert.equal(initialResponse.status, 200); + assert.equal(await initialResponse.text(), '// this file is cached'); + assert.equal(typeof etag, 'string'); await timeout(10); const fileContent = `// the cache is busted${nanoid()}`; @@ -78,8 +79,8 @@ describe('etag cache middleware', () => { const headers = { headers: { 'if-none-match': etag } as Record }; const cachedResponse = await fetch(`${host}${testFileBName}`, headers); - expect(cachedResponse.status).to.equal(200); - expect(await cachedResponse.text()).to.equal(fileContent); + assert.equal(cachedResponse.status, 200); + assert.equal(await cachedResponse.text(), fileContent); }); }); }); diff --git a/packages/dev-server-core/test/middleware/historyApiFallbackMiddleware.test.ts b/packages/dev-server-core/test/middleware/historyApiFallbackMiddleware.test.ts index ba511b2f2f..609f6411c9 100644 --- a/packages/dev-server-core/test/middleware/historyApiFallbackMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/historyApiFallbackMiddleware.test.ts @@ -1,8 +1,9 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it, beforeEach, afterEach } from 'node:test'; import path from 'path'; -import { createTestServer } from '../helpers.js'; -import { DevServer } from '../../src/server/DevServer.js'; +import { createTestServer } from '../helpers.ts'; +import type { DevServer } from '../../dist/server/DevServer.js'; describe('history api fallback middleware', () => { describe('index in root', () => { @@ -11,7 +12,7 @@ describe('history api fallback middleware', () => { beforeEach(async () => { ({ host, server } = await createTestServer({ - appIndex: path.resolve(__dirname, '..', 'fixtures', 'basic', 'index.html'), + appIndex: path.resolve(import.meta.dirname, '..', 'fixtures', 'basic', 'index.html'), })); }); @@ -23,41 +24,41 @@ describe('history api fallback middleware', () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); it('returns the fallback index.html for non-file requests', async () => { const response = await fetch(`${host}/foo`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); it('returns the fallback index.html for file requests with multiple segments', async () => { const response = await fetch(`${host}/foo/bar/baz`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); it('does not return index.html for file requests', async () => { const response = await fetch(`${host}/src/hello-world.txt`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('Hello world!'); - expect(responseText).to.not.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('Hello world!')); + assert.ok(!responseText.includes('My app')); }); it('does return index.html for requests that have url parameters with . characters (issue 1059)', async () => { const response = await fetch(`${host}/text-files/foo/bar/?baz=open.wc`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); }); @@ -67,7 +68,7 @@ describe('history api fallback middleware', () => { beforeEach(async () => { ({ host, server } = await createTestServer({ - appIndex: path.resolve(__dirname, '..', 'fixtures', 'basic', 'src', 'index.html'), + appIndex: path.resolve(import.meta.dirname, '..', 'fixtures', 'basic', 'src', 'index.html'), })); }); @@ -79,40 +80,40 @@ describe('history api fallback middleware', () => { const response = await fetch(`${host}/src/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app 2'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app 2')); }); it('returns the fallback index.html for non-file requests', async () => { const response = await fetch(`${host}/src/foo`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app 2'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app 2')); }); it('returns the fallback index.html for file requests with multiple segments', async () => { const response = await fetch(`${host}/src/foo/bar/baz`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app 2'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app 2')); }); it('does not return the index.html for requests outside the index root', async () => { const response = await fetch(`${host}/foo`); const responseText = await response.text(); - expect(response.status).to.equal(404); - expect(responseText).to.not.include('My app 2'); + assert.equal(response.status, 404); + assert.ok(!responseText.includes('My app 2')); }); it('does return index.html for requests that have url parameters with . characters (issue 1059)', async () => { const response = await fetch(`${host}/src/foo/bar/?baz=open.wc`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app 2'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app 2')); }); }); }); diff --git a/packages/dev-server-core/test/middleware/pluginFileParsedMiddleware.test.ts b/packages/dev-server-core/test/middleware/pluginFileParsedMiddleware.test.ts index 844287d97a..f9098309a1 100644 --- a/packages/dev-server-core/test/middleware/pluginFileParsedMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/pluginFileParsedMiddleware.test.ts @@ -1,7 +1,8 @@ -import { expect } from 'chai'; -import { Context } from 'koa'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import type { Context } from 'koa'; -import { createTestServer } from '../helpers.js'; +import { createTestServer } from '../helpers.ts'; describe('plugin-file-parsed middleware', () => { it('is called after other plugin hooks', async () => { @@ -44,11 +45,11 @@ describe('plugin-file-parsed middleware', () => { try { const response = await fetch(`${host}/foo.js`); - expect(response.status).to.equal(200); - expect(response.headers.get('content-type')).to.include('application/javascript'); - expect(order[order.length - 1]).to.equal('fileParsed'); - expect(context).to.exist; - expect(context!.path).to.equal('/foo.js'); + assert.equal(response.status, 200); + assert.ok(response.headers.get('content-type')?.includes('application/javascript')); + assert.equal(order[order.length - 1], 'fileParsed'); + assert.ok(context); + assert.equal(context!.path, '/foo.js'); } finally { server.stop(); } diff --git a/packages/dev-server-core/test/middleware/pluginMimeTypeMiddleware.test.ts b/packages/dev-server-core/test/middleware/pluginMimeTypeMiddleware.test.ts index 49e7c55a5c..3bf9da7d51 100644 --- a/packages/dev-server-core/test/middleware/pluginMimeTypeMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/pluginMimeTypeMiddleware.test.ts @@ -1,6 +1,7 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import { createTestServer } from '../helpers.js'; +import { createTestServer } from '../helpers.ts'; describe('plugin-mime-type middleware', () => { it('can set the mime type of a file with a string', async () => { @@ -20,8 +21,8 @@ describe('plugin-mime-type middleware', () => { try { const response = await fetch(`${host}/src/hello-world.txt`); - expect(response.status).to.equal(200); - expect(response.headers.get('content-type')).to.include('application/javascript'); + assert.equal(response.status, 200); + assert.ok(response.headers.get('content-type')?.includes('application/javascript')); } finally { server.stop(); } @@ -44,8 +45,8 @@ describe('plugin-mime-type middleware', () => { try { const response = await fetch(`${host}/src/hello-world.txt`); - expect(response.status).to.equal(200); - expect(response.headers.get('content-type')).to.include('application/javascript'); + assert.equal(response.status, 200); + assert.ok(response.headers.get('content-type')?.includes('application/javascript')); } finally { server.stop(); } diff --git a/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts b/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts index baec7e2faf..e7a34a7bd3 100644 --- a/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts @@ -1,6 +1,7 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import { createTestServer } from '../helpers.js'; +import { createTestServer } from '../helpers.ts'; describe('plugin-serve middleware', () => { it('can serve non-existing files', async () => { @@ -21,8 +22,8 @@ describe('plugin-serve middleware', () => { const response = await fetch(`${host}/non-existing.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('serving non-existing.js'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('serving non-existing.js')); } finally { server.stop(); } @@ -54,8 +55,8 @@ describe('plugin-serve middleware', () => { const response = await fetch(`${host}/non-existing.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('serve a'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('serve a')); } finally { server.stop(); } @@ -79,9 +80,10 @@ describe('plugin-serve middleware', () => { const response = await fetch(`${host}/non-existing.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('serving non-existing.js'); - expect(response.headers.get('content-type')).to.equal( + assert.equal(response.status, 200); + assert.ok(responseText.includes('serving non-existing.js')); + assert.equal( + response.headers.get('content-type'), 'application/javascript; charset=utf-8', ); } finally { @@ -105,8 +107,8 @@ describe('plugin-serve middleware', () => { try { const response = await fetch(`${host}/foo.bar`); - expect(response.status).to.equal(200); - expect(response.headers.get('content-type')).to.equal('text/css; charset=utf-8'); + assert.equal(response.status, 200); + assert.equal(response.headers.get('content-type'), 'text/css; charset=utf-8'); } finally { server.stop(); } @@ -130,8 +132,8 @@ describe('plugin-serve middleware', () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('overwritten index.html'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('overwritten index.html')); } finally { server.stop(); } @@ -154,8 +156,8 @@ describe('plugin-serve middleware', () => { try { const response = await fetch(`${host}/index.html`); - expect(response.status).to.equal(200); - expect(response.headers.get('x-foo')).to.equal('bar'); + assert.equal(response.status, 200); + assert.equal(response.headers.get('x-foo'), 'bar'); } finally { server.stop(); } @@ -179,8 +181,8 @@ describe('plugin-serve middleware', () => { const response = await fetch(`${host}/non-existing.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('serving non-existing.js'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('serving non-existing.js')); } finally { server.stop(); } diff --git a/packages/dev-server-core/test/middleware/pluginTransformMiddleware.test.ts b/packages/dev-server-core/test/middleware/pluginTransformMiddleware.test.ts index d0fc91f2b8..2a4fd101ba 100644 --- a/packages/dev-server-core/test/middleware/pluginTransformMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/pluginTransformMiddleware.test.ts @@ -1,8 +1,9 @@ /* eslint-disable no-restricted-syntax, no-await-in-loop */ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import { createTestServer } from '../helpers.js'; -import { fetchText, expectIncludes } from '../../src/test-helpers.js'; +import { createTestServer } from '../helpers.ts'; +import { fetchText, expectIncludes } from '../../dist/test-helpers.js'; describe('plugin-transform middleware', () => { it('can transform a served file', async () => { @@ -23,8 +24,8 @@ describe('plugin-transform middleware', () => { const response = await fetch(`${host}/src/hello-world.txt`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.equal('Hello world! injected text'); + assert.equal(response.status, 200); + assert.equal(responseText, 'Hello world! injected text'); } finally { server.stop(); } @@ -54,8 +55,8 @@ describe('plugin-transform middleware', () => { const response = await fetch(`${host}/non-existing.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.equal('my non existing file injected text'); + assert.equal(response.status, 200); + assert.equal(responseText, 'my non existing file injected text'); } finally { server.stop(); } @@ -87,8 +88,8 @@ describe('plugin-transform middleware', () => { const response = await fetch(`${host}/src/hello-world.txt`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.equal('Hello world! INJECT_A INJECT_B'); + assert.equal(response.status, 200); + assert.equal(responseText, 'Hello world! INJECT_A INJECT_B'); } finally { server.stop(); } @@ -112,8 +113,8 @@ describe('plugin-transform middleware', () => { const response = await fetch(`${host}/non-existing.js`); const responseText = await response.text(); - expect(response.status).to.equal(404); - expect(responseText).to.equal('Not Found'); + assert.equal(response.status, 404); + assert.equal(responseText, 'Not Found'); } finally { server.stop(); } @@ -136,8 +137,8 @@ describe('plugin-transform middleware', () => { try { const response = await fetch(`${host}/index.html`); - expect(response.status).to.equal(200); - expect(response.headers.get('x-foo')).to.equal('bar'); + assert.equal(response.status, 200); + assert.equal(response.headers.get('x-foo'), 'bar'); } finally { server.stop(); } @@ -164,7 +165,7 @@ describe('plugin-transform middleware', () => { const responseTwo = await fetch(`${host}/src/hello-world.txt`); const timestampTwo = await responseTwo.text(); - expect(timestampOne).equal(timestampTwo); + assert.equal(timestampOne, timestampTwo); } finally { server.stop(); } @@ -195,10 +196,11 @@ describe('plugin-transform middleware', () => { const textTwo = await responseTwo.text(); const headersTwo = responseTwo.headers; - expect(textOne).equal('console.log("foo")'); - expect(textTwo).equal('console.log("foo")'); - expect(headersOne.get('x-foo')).eql('bar'); - expect(Object.fromEntries(headersOne.entries())).eql( + assert.equal(textOne, 'console.log("foo")'); + assert.equal(textTwo, 'console.log("foo")'); + assert.equal(headersOne.get('x-foo'), 'bar'); + assert.deepEqual( + Object.fromEntries(headersOne.entries()), Object.fromEntries(headersTwo.entries()), ); } finally { @@ -227,7 +229,7 @@ describe('plugin-transform middleware', () => { const responseTwo = await fetch(`${host}/src/hello-world.txt`); const timestampTwo = await responseTwo.text(); - expect(timestampOne).to.not.equal(timestampTwo); + assert.notEqual(timestampOne, timestampTwo); } finally { server.stop(); } @@ -251,8 +253,8 @@ describe('plugin-transform middleware', () => { const response = await fetch(`${host}/src/hello-world.txt`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.equal('Hello world! injected text'); + assert.equal(response.status, 200); + assert.equal(responseText, 'Hello world! injected text'); } finally { server.stop(); } @@ -289,7 +291,6 @@ describe('plugin-transform middleware', () => { }); try { - // response is transformed based on user agent const responseA1 = await fetchText(`${host}/src/hello-world.txt`, { headers: { 'user-agent': 'agent-a' }, }); @@ -300,18 +301,17 @@ describe('plugin-transform middleware', () => { }); expectIncludes(responseB1, 'Hello world! injected text B 1'); - // A1 and B1 are now cached separately, we should receive them based on user agent const responseA2 = await fetchText(`${host}/src/hello-world.txt`, { headers: { 'user-agent': 'agent-a' }, }); expectIncludes(responseA2, 'Hello world! injected text A 1'); - expect(callCountA).to.equal(1); + assert.equal(callCountA, 1); const responseB2 = await fetchText(`${host}/src/hello-world.txt`, { headers: { 'user-agent': 'agent-b' }, }); expectIncludes(responseB2, 'Hello world! injected text B 1'); - expect(callCountB).to.equal(1); + assert.equal(callCountB, 1); } finally { server.stop(); } diff --git a/packages/dev-server-core/test/middleware/serveFilesMiddleware.test.ts b/packages/dev-server-core/test/middleware/serveFilesMiddleware.test.ts index 453d11d2b1..428f24a619 100644 --- a/packages/dev-server-core/test/middleware/serveFilesMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/serveFilesMiddleware.test.ts @@ -1,14 +1,15 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import path from 'path'; -import { createTestServer } from '../helpers.js'; +import { createTestServer } from '../helpers.ts'; describe('serveFilesMiddleware', () => { it('can serve files outside of the root directory', async () => { const { host, server } = await createTestServer({ plugins: [{ name: 'test' }], rootDir: path.resolve( - __dirname, + import.meta.dirname, '..', 'fixtures', 'outside-root-dir', @@ -21,8 +22,8 @@ describe('serveFilesMiddleware', () => { const response = await fetch(`${host}/__wds-outside-root__/2/node_modules/foo/index.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include("export default 'foo'"); + assert.equal(response.status, 200); + assert.ok(responseText.includes("export default 'foo'")); } finally { server.stop(); } diff --git a/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts b/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts index 6411081904..244faf0b0d 100644 --- a/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts +++ b/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts @@ -1,6 +1,7 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import { createTestServer } from '../helpers.js'; +import { createTestServer } from '../helpers.ts'; describe('mimeTypesPLugin', () => { it('can configure mime types for files', async () => { @@ -22,8 +23,9 @@ describe('mimeTypesPLugin', () => { try { const response = await fetch(`${host}/foo.css`); - expect(response.status).to.equal(200); - expect(response.headers.get('content-type')).to.equal( + assert.equal(response.status, 200); + assert.equal( + response.headers.get('content-type'), 'application/javascript; charset=utf-8', ); } finally { @@ -33,7 +35,7 @@ describe('mimeTypesPLugin', () => { it('can resolve literal paths', async () => { const { server, host } = await createTestServer({ - rootDir: __dirname, + rootDir: import.meta.dirname, mimeTypes: { 'foo.css': 'js', }, @@ -51,14 +53,16 @@ describe('mimeTypesPLugin', () => { try { const responseA = await fetch(`${host}/foo.css`); - expect(responseA.status).to.equal(200); - expect(responseA.headers.get('content-type')).to.equal( + assert.equal(responseA.status, 200); + assert.equal( + responseA.headers.get('content-type'), 'application/javascript; charset=utf-8', ); const responseB = await fetch(`${host}/x/foo.css`); - expect(responseB.status).to.equal(200); - expect(responseB.headers.get('content-type')).not.to.equal( + assert.equal(responseB.status, 200); + assert.notEqual( + responseB.headers.get('content-type'), 'application/javascript; charset=utf-8', ); } finally { diff --git a/packages/dev-server-core/test/plugins/parseDynamicImport.test.ts b/packages/dev-server-core/test/plugins/parseDynamicImport.test.ts index 39b7bbb0d0..875c3a9164 100644 --- a/packages/dev-server-core/test/plugins/parseDynamicImport.test.ts +++ b/packages/dev-server-core/test/plugins/parseDynamicImport.test.ts @@ -1,5 +1,6 @@ -import { expect } from 'chai'; -import { parseDynamicImport } from '../../src/plugins/parseDynamicImport.js'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { parseDynamicImport } from '../../dist/plugins/parseDynamicImport.js'; describe('parseDynamicImport', () => { function testParseDynamicImport(specifier: string) { @@ -9,7 +10,7 @@ describe('parseDynamicImport', () => { it('works for " string literals', () => { const importStringA = '"./a.js"'; - expect(testParseDynamicImport(importStringA)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringA), { importString: importStringA, importSpecifier: './a.js', concatenatedString: false, @@ -19,7 +20,7 @@ describe('parseDynamicImport', () => { }); const importStringB = '"./a/b/c.js"'; - expect(testParseDynamicImport(importStringB)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringB), { importString: importStringB, importSpecifier: './a/b/c.js', concatenatedString: false, @@ -29,7 +30,7 @@ describe('parseDynamicImport', () => { }); const importStringC = '"/a/b.js"'; - expect(testParseDynamicImport(importStringC)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringC), { importString: importStringC, importSpecifier: '/a/b.js', concatenatedString: false, @@ -41,7 +42,7 @@ describe('parseDynamicImport', () => { it("works for ' string literals", () => { const importString = "'./a.js'"; - expect(testParseDynamicImport(importString)).to.eql({ + assert.deepEqual(testParseDynamicImport(importString), { importString, importSpecifier: './a.js', concatenatedString: false, @@ -53,7 +54,7 @@ describe('parseDynamicImport', () => { it('works for singlecharacter string literals', () => { const importString = "'a.js'"; - expect(testParseDynamicImport(importString)).to.eql({ + assert.deepEqual(testParseDynamicImport(importString), { importString, importSpecifier: 'a.js', concatenatedString: false, @@ -65,7 +66,7 @@ describe('parseDynamicImport', () => { it('works for ` string literals', () => { const importString = '`./a/b.js`'; - expect(testParseDynamicImport(importString)).to.eql({ + assert.deepEqual(testParseDynamicImport(importString), { importString, importSpecifier: './a/b.js', concatenatedString: true, @@ -77,7 +78,7 @@ describe('parseDynamicImport', () => { it('works for concatenated strings', () => { const importStringA = "'./a' + '.js'"; - expect(testParseDynamicImport(importStringA)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringA), { importString: importStringA, importSpecifier: "./a' + '.js", concatenatedString: true, @@ -87,7 +88,7 @@ describe('parseDynamicImport', () => { }); const importStringB = '"./a" + "/b/" + "c.js"'; - expect(testParseDynamicImport(importStringB)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringB), { importString: importStringB, importSpecifier: './a" + "/b/" + "c.js', concatenatedString: true, @@ -99,7 +100,7 @@ describe('parseDynamicImport', () => { it('works for interpolated string literals', () => { const importString = '`./a/${file}.js`'; - expect(testParseDynamicImport(importString)).to.eql({ + assert.deepEqual(testParseDynamicImport(importString), { importString, importSpecifier: './a/${file}.js', concatenatedString: true, @@ -111,7 +112,7 @@ describe('parseDynamicImport', () => { it('works for variables', () => { const importString = 'foo'; - expect(testParseDynamicImport(importString)).to.eql({ + assert.deepEqual(testParseDynamicImport(importString), { importString, importSpecifier: 'foo', concatenatedString: false, @@ -123,7 +124,7 @@ describe('parseDynamicImport', () => { it('works for single character variables', () => { const importString = 'a'; - expect(testParseDynamicImport(importString)).to.eql({ + assert.deepEqual(testParseDynamicImport(importString), { importString, importSpecifier: 'a', concatenatedString: false, @@ -135,7 +136,7 @@ describe('parseDynamicImport', () => { it('works for variables with concatenation', () => { const importString = 'foo + "x.js"'; - expect(testParseDynamicImport(importString)).to.eql({ + assert.deepEqual(testParseDynamicImport(importString), { importString, importSpecifier: 'foo + "x.js"', concatenatedString: true, @@ -147,7 +148,7 @@ describe('parseDynamicImport', () => { it('works for string literals with spaces or newlines', () => { const importStringA = ' "./a.js" '; - expect(testParseDynamicImport(importStringA)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringA), { importString: '"./a.js"', importSpecifier: './a.js', concatenatedString: false, @@ -157,7 +158,7 @@ describe('parseDynamicImport', () => { }); const importStringB = '\n "./a.js"\n '; - expect(testParseDynamicImport(importStringB)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringB), { importString: '"./a.js"', importSpecifier: './a.js', concatenatedString: false, @@ -169,7 +170,7 @@ describe('parseDynamicImport', () => { it('works for template strings with spaces or newlines', () => { const importStringA = '\n `./x/${file}.js`\n '; - expect(testParseDynamicImport(importStringA)).to.eql({ + assert.deepEqual(testParseDynamicImport(importStringA), { importString: '`./x/${file}.js`', importSpecifier: './x/${file}.js', concatenatedString: true, diff --git a/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts b/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts index f1ce647d2c..f5b935f79c 100644 --- a/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts +++ b/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts @@ -1,8 +1,9 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import { transformImports } from '../../src/plugins/transformModuleImportsPlugin.js'; -import type { PluginSyntaxError } from '../../src/logger/PluginSyntaxError.js'; -import { createTestServer } from '../helpers.js'; +import { transformImports } from '../../dist/plugins/transformModuleImportsPlugin.js'; +import type { PluginSyntaxError } from '../../dist/logger/PluginSyntaxError.js'; +import { createTestServer } from '../helpers.ts'; const defaultFilePath = '/root/my-file.js'; const defaultResolveImport = (src: string) => `RESOLVED__${src}`; @@ -21,7 +22,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'import "RESOLVED__my-module";', 'import foo from "RESOLVED__my-module";', 'import { bar } from "RESOLVED__my-module";', @@ -41,7 +42,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ // "export * from 'RESOLVED__my-module';", "export { foo } from 'RESOLVED__my-module';", @@ -55,7 +56,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result).to.eql("import 'RESOLVED__my-module/bar/index.js"); + assert.equal(result, "import 'RESOLVED__my-module/bar/index.js"); }); it('resolves dynamic imports', async () => { @@ -70,7 +71,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'import("RESOLVED__/bar.js");', // 'function lazyLoad() { return import("RESOLVED__my-module-2"); }', // 'import("RESOLVED__my-module");', @@ -89,7 +90,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'console.log(import.meta.url);', "import 'RESOLVED__my-module';", ]); @@ -106,7 +107,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ "import 'RESOLVED__my-module';", "// Example: import('my-module');", ]); @@ -125,7 +126,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'function myimport() { }', 'function my_import() { }', 'function importShim() { }', @@ -147,7 +148,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'import(`RESOLVED__@namespace/my-module-3/dynamic-files/${file}.js`);', 'import(`RESOLVED__my-module/dynamic-files/${file}.js`);', 'import("RESOLVED__my-module/dynamic-files" + "/" + file + ".js");', @@ -167,7 +168,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'import("RESOLVED__./a.js");', "import('RESOLVED__./b.js');", ]); @@ -187,7 +188,7 @@ describe('transformImports()', () => { defaultFilePath, defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'import( "RESOLVED__./a.js" );', 'import( "RESOLVED__./b.js" );', 'import( "./c" + ".js" );', @@ -211,7 +212,7 @@ describe('transformImports()', () => { ); }); - it('does not change import with string concatenation cannot be resolved', async () => { + it('does not change import with string concatenation cannot be resolved (2)', async () => { await transformImports( [ 'const file = "a";', @@ -240,7 +241,7 @@ describe('transformImports()', () => { defaultResolveImport, ); - expect(result.split('\n')).to.eql([ + assert.deepEqual(result.split('\n'), [ 'import(`./foo/${file}.js`);', 'import(`/${file}.js`);', 'import("./foo" + "/" + file + ".js");', @@ -251,19 +252,16 @@ describe('transformImports()', () => { }); it('throws a syntax error on invalid imports', async () => { - let thrown = false; - - try { - await transformImports('\n\nconst file = "a', defaultFilePath, defaultResolveImport); - } catch (error) { - thrown = true; - expect((error as PluginSyntaxError).message).to.equal('Syntax error'); - expect((error as PluginSyntaxError).filePath).to.equal('/root/my-file.js'); - expect((error as PluginSyntaxError).column).to.equal(16); - expect((error as PluginSyntaxError).line).to.equal(3); - } - - expect(thrown).to.be.true; + await assert.rejects( + () => transformImports('\n\nconst file = "a', defaultFilePath, defaultResolveImport), + (error: PluginSyntaxError) => { + assert.equal(error.message, 'Syntax error'); + assert.equal(error.filePath, '/root/my-file.js'); + assert.equal(error.column, 16); + assert.equal(error.line, 3); + return true; + }, + ); }); }); @@ -284,8 +282,8 @@ describe('resolveImport', () => { const response = await fetch(`${host}/src/app.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include("import { message } from 'RESOLVED__my-module';"); + assert.equal(response.status, 200); + assert.ok(responseText.includes("import { message } from 'RESOLVED__my-module';")); } finally { server.stop(); } @@ -307,8 +305,8 @@ describe('resolveImport', () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include("import { message } from 'RESOLVED__my-module';"); + assert.equal(response.status, 200); + assert.ok(responseText.includes("import { message } from 'RESOLVED__my-module';")); } finally { server.stop(); } @@ -330,8 +328,8 @@ describe('resolveImport', () => { const response = await fetch(`${host}/src/app.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include("import { message } from 'my-module';"); + assert.equal(response.status, 200); + assert.ok(responseText.includes("import { message } from 'my-module';")); } finally { server.stop(); } @@ -363,10 +361,10 @@ describe('resolveImport', () => { const responseTextA = await responseA.text(); const responseTextB = await responseB.text(); - expect(responseA.status).to.equal(200); - expect(responseB.status).to.equal(200); - expect(responseTextA).to.include("import { message } from 'RESOLVED__A__my-module';"); - expect(responseTextB).to.include("import { message } from 'RESOLVED__B__my-module';"); + assert.equal(responseA.status, 200); + assert.equal(responseB.status, 200); + assert.ok(responseTextA.includes("import { message } from 'RESOLVED__A__my-module';")); + assert.ok(responseTextB.includes("import { message } from 'RESOLVED__B__my-module';")); } finally { server.stop(); } @@ -390,9 +388,9 @@ describe('transformImport', () => { const response = await fetch(`${host}/src/app.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include("import { message } from 'my-module?transformed-1';"); - expect(responseText).to.include('./src/local-module.js?transformed-1'); + assert.equal(response.status, 200); + assert.ok(responseText.includes("import { message } from 'my-module?transformed-1';")); + assert.ok(responseText.includes('./src/local-module.js?transformed-1')); } finally { server.stop(); } @@ -420,11 +418,13 @@ describe('transformImport', () => { const response = await fetch(`${host}/src/app.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include( - "import { message } from 'my-module?transformed-1&transformed-2';", + assert.equal(response.status, 200); + assert.ok( + responseText.includes( + "import { message } from 'my-module?transformed-1&transformed-2';", + ), ); - expect(responseText).to.include('./src/local-module.js?transformed-1&transformed-2'); + assert.ok(responseText.includes('./src/local-module.js?transformed-1&transformed-2')); } finally { server.stop(); } @@ -458,11 +458,13 @@ describe('transformImport', () => { const response = await fetch(`${host}/src/app.js`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include( - "import { message } from 'my-module?transformed-1&transformed-2';", + assert.equal(response.status, 200); + assert.ok( + responseText.includes( + "import { message } from 'my-module?transformed-1&transformed-2';", + ), ); - expect(responseText).to.include('./src/local-module.js?transformed-1&transformed-2'); + assert.ok(responseText.includes('./src/local-module.js?transformed-1&transformed-2')); } finally { server.stop(); } @@ -490,7 +492,7 @@ describe('transformImport', () => { try { await fetch(`${host}/src/app.js`); - expect(receivedImports).to.eql(['RESOLVED__my-module', 'RESOLVED__./src/local-module.js']); + assert.deepEqual(receivedImports, ['RESOLVED__my-module', 'RESOLVED__./src/local-module.js']); } finally { server.stop(); } diff --git a/packages/dev-server-core/test/server/DevServer.test.ts b/packages/dev-server-core/test/server/DevServer.test.ts index 6725579117..98db4f7fce 100644 --- a/packages/dev-server-core/test/server/DevServer.test.ts +++ b/packages/dev-server-core/test/server/DevServer.test.ts @@ -1,14 +1,13 @@ +import assert from 'node:assert/strict'; +import { describe, it, beforeEach, afterEach, before, after, mock } from 'node:test'; import express from 'express'; import http from 'http'; import Koa from 'koa'; import { Server } from 'net'; -import { FSWatcher } from 'chokidar'; -import { expect } from 'chai'; import portfinder from 'portfinder'; -import { Stub, stubMethod } from 'hanbi'; -import { ServerStartParams } from '../../src/plugins/Plugin.js'; -import { DevServer } from '../../src/server/DevServer.js'; -import { createTestServer } from '../helpers.js'; +import type { ServerStartParams } from '../../dist/plugins/Plugin.js'; +import type { DevServer } from '../../dist/server/DevServer.js'; +import { createTestServer } from '../helpers.ts'; describe('basic', () => { let host: string; @@ -26,37 +25,37 @@ describe('basic', () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); }); it('returns hidden files', async () => { const response = await fetch(`${host}/.hidden`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('this file is hidden'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('this file is hidden')); }); it('returns files in a folder', async () => { const response = await fetch(`${host}/src/hello-world.txt`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.equal('Hello world!'); + assert.equal(response.status, 200); + assert.equal(responseText, 'Hello world!'); }); it('returns a 404 for unknown files', async () => { const response = await fetch(`${host}/non-existing.js`); - expect(response.status).to.equal(404); + assert.equal(response.status, 404); }); it('sets no-cache header', async () => { const response = await fetch(`${host}/index.html`); - expect(response.status).to.equal(200); - expect(response.headers.get('cache-control')).to.equal('no-cache'); + assert.equal(response.status, 200); + assert.equal(response.headers.get('cache-control'), 'no-cache'); }); }); @@ -65,18 +64,13 @@ it('can configure the hostname', async () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); server.stop(); }); describe('http2', () => { before(() => { - // Turn off the TLS authorized check in node.js so that we don't reject the network response - // based off the fact it has a self-signed certificate. - // - // A better way to achive this might be to _somehow_ load up the certificate used into the - // testing process so that we aren't just turning off the TLS/SSL certificate validation. process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; }); @@ -89,19 +83,15 @@ describe('http2', () => { const response = await fetch(`${host}/index.html`); const responseText = await response.text(); - // It's a bit of a shame that we can't verify that the response was delivered with a http/2 - // protocol. It would be good to have a extra assertion here. Something like: - // - // expect(response.protocol).to.equal('http2'); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); server.stop(); }); }); it('can run in middleware mode', async () => { const { server: wdsServer } = await createTestServer({ middlewareMode: true }); - expect(wdsServer.server).to.equal(undefined); + assert.equal(wdsServer.server, undefined); const app = express(); let httpServer: http.Server; @@ -117,8 +107,8 @@ it('can run in middleware mode', async () => { const response = await fetch(`http://localhost:${port}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); httpServer!.close(); }); @@ -136,8 +126,8 @@ it('can run multiple servers in parallel', async () => { const response = await fetch(`${result.host}/index.html`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('My app'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('My app')); result.server.stop(); } }); @@ -157,8 +147,8 @@ it('can add extra middleware', async () => { const response = await fetch(`${host}/foo`); const responseText = await response.text(); - expect(response.status).to.equal(200); - expect(responseText).to.include('response from middleware'); + assert.equal(response.status, 200); + assert.ok(responseText.includes('response from middleware')); server.stop(); }); @@ -175,11 +165,11 @@ it('calls serverStart on plugin hook on start', async () => { ], }); - expect(startArgs!).to.exist; - expect(startArgs!.app).to.be.an.instanceOf(Koa); - expect(startArgs!.server).to.be.an.instanceOf(Server); - expect(startArgs!.fileWatcher).to.be.an.instanceOf(FSWatcher); - expect(startArgs!.config).to.be.an('object'); + assert.ok(startArgs!); + assert.ok(startArgs!.app instanceof Koa); + assert.ok(startArgs!.server instanceof Server); + assert.equal(startArgs!.fileWatcher.constructor.name, 'FSWatcher'); + assert.equal(typeof startArgs!.config, 'object'); server.stop(); }); @@ -198,7 +188,7 @@ it('calls serverStop on plugin hook on stop', async () => { }); await server.stop(); - expect(stopCalled).to.be.true; + assert.equal(stopCalled, true); }); it('waits on server start hooks before starting', async () => { @@ -210,7 +200,7 @@ it('waits on server start hooks before starting', async () => { { name: 'test-a', serverStart() { - return new Promise(resolve => + return new Promise(resolve => setTimeout(() => { aFinished = true; resolve(); @@ -221,7 +211,7 @@ it('waits on server start hooks before starting', async () => { { name: 'test-b', serverStart() { - return new Promise(resolve => + return new Promise(resolve => setTimeout(() => { bFinished = true; resolve(); @@ -232,52 +222,45 @@ it('waits on server start hooks before starting', async () => { ], }); - expect(aFinished).to.be.true; - expect(bFinished).to.be.true; + assert.equal(aFinished, true); + assert.equal(bFinished, true); server.stop(); }); describe('disableFileWatcher', () => { - /** - * Extracted setup to ensure `fileWatcher.add` is called when - * `disableFileWatch = false`. Only then there is confidence that the - * `disableFileWatch = true` actually works. - * */ const setupDisableFileWatch = async (config: { disableFileWatcher: boolean }) => { - let fileWatchStub: Stub; + let addMock: ReturnType | undefined; const { host, server } = await createTestServer({ disableFileWatcher: config.disableFileWatcher, plugins: [ { name: 'watcher-stub', serverStart({ fileWatcher }) { - fileWatchStub = stubMethod(fileWatcher, 'add'); + addMock = mock.method(fileWatcher, 'add'); }, }, ], }); - // @ts-ignore - if (!fileWatchStub) { - throw new Error('Something went wrong with stubbing the file watcher'); + if (!addMock) { + throw new Error('Something went wrong with mocking the file watcher'); } - // Ensure something is fetched to trigger all the middlewares await fetch(`${host}/index.html`); - return { fileWatchStub, host, server }; + return { addMock, host, server }; }; it('disables file watch when true', async () => { - const { fileWatchStub, server } = await setupDisableFileWatch({ disableFileWatcher: true }); + const { addMock, server } = await setupDisableFileWatch({ disableFileWatcher: true }); - expect(fileWatchStub.callCount).to.equal(0); + assert.equal(addMock.mock.callCount(), 0); server.stop(); }); it('leaves file watch in tact when false', async () => { - const { fileWatchStub, server } = await setupDisableFileWatch({ disableFileWatcher: false }); + const { addMock, server } = await setupDisableFileWatch({ disableFileWatcher: false }); - expect(fileWatchStub.callCount).to.gt(0); + assert.ok(addMock.mock.callCount() > 0); server.stop(); }); }); diff --git a/packages/dev-server-core/test/web-sockets/WebSocketsManager.test.ts b/packages/dev-server-core/test/web-sockets/WebSocketsManager.test.ts index 98d3d97208..803bfa658a 100644 --- a/packages/dev-server-core/test/web-sockets/WebSocketsManager.test.ts +++ b/packages/dev-server-core/test/web-sockets/WebSocketsManager.test.ts @@ -1,8 +1,9 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; import WebSocket from 'ws'; -import { NAME_WEB_SOCKET_API } from '../../src/web-sockets/WebSocketsManager.js'; +import { NAME_WEB_SOCKET_API } from '../../dist/web-sockets/WebSocketsManager.js'; -import { createTestServer } from '../helpers.js'; +import { createTestServer } from '../helpers.ts'; function waitFor(fn: (resolve: () => void) => void, msg: string) { return new Promise((resolve, reject) => { @@ -56,7 +57,7 @@ describe('WebSocketManager', () => { const waitForMessage = waitFor(resolve => { ws.on('message', data => { - expect(data).to.equal('hello world'); + assert.equal(data, 'hello world'); resolve(); }); }, 'expected a message event'); @@ -91,13 +92,13 @@ describe('WebSocketManager', () => { const waitForMessage1 = waitFor(resolve => { ws1.on('message', data => { - expect(data).to.equal('hello world'); + assert.equal(data, 'hello world'); resolve(); }); }, 'expected message'); const waitForMessage2 = waitFor(resolve => { ws1.on('message', data => { - expect(data).to.equal('hello world'); + assert.equal(data, 'hello world'); resolve(); }); }, 'expected message'); @@ -126,8 +127,8 @@ describe('WebSocketManager', () => { const waitForMessage = waitFor(resolve => { server.webSockets!.on('message', ({ webSocket, data }) => { - expect(webSocket).to.be.an.instanceOf(WebSocket); - expect(data).to.eql({ type: 'foo' }); + assert.ok(webSocket instanceof WebSocket); + assert.deepEqual(data, { type: 'foo' }); resolve(); }); }, 'expected message event fired from manager'); @@ -156,7 +157,7 @@ describe('WebSocketManager', () => { const waitForMessage = waitFor(resolve => { ws.on('message', data => { const parsedData = JSON.parse(data as any); - expect(parsedData).to.eql({ + assert.deepEqual(parsedData, { data: { args: [], importPath: 'data:text/javascript,console.log("hello world");', @@ -191,7 +192,7 @@ describe('WebSocketManager', () => { const waitForMessage = waitFor(resolve => { ws.on('message', data => { const parsedData = JSON.parse(data as any); - expect(parsedData).to.eql({ + assert.deepEqual(parsedData, { data: { args: [], importPath: '/foo.js', diff --git a/packages/dev-server-core/test/web-sockets/webSocketsPlugin.test.ts b/packages/dev-server-core/test/web-sockets/webSocketsPlugin.test.ts index 8c59fda77b..1ca5bf5a65 100644 --- a/packages/dev-server-core/test/web-sockets/webSocketsPlugin.test.ts +++ b/packages/dev-server-core/test/web-sockets/webSocketsPlugin.test.ts @@ -1,7 +1,8 @@ -import { expect } from 'chai'; +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; -import { createTestServer } from '../helpers.js'; -import { webSocketScript } from '../../src/web-sockets/webSocketsPlugin.js'; +import { createTestServer } from '../helpers.ts'; +import { webSocketScript } from '../../dist/web-sockets/webSocketsPlugin.js'; describe('webSocketsPlugin', () => { it('injects an event stream script if a plugin has inject set and event stream is enabled', async () => { @@ -19,8 +20,8 @@ describe('webSocketsPlugin', () => { const response = await fetch(`${host}/index.html`); const body = await response.text(); - expect(response.status).to.equal(200); - expect(body).to.include(webSocketScript); + assert.equal(response.status, 200); + assert.ok(body.includes(webSocketScript)); } finally { server.stop(); } @@ -41,8 +42,8 @@ describe('webSocketsPlugin', () => { const response = await fetch(`${host}/index.html`); const body = await response.text(); - expect(response.status).to.equal(200); - expect(body).to.not.include(webSocketScript); + assert.equal(response.status, 200); + assert.ok(!body.includes(webSocketScript)); } finally { server.stop(); } @@ -54,8 +55,8 @@ describe('webSocketsPlugin', () => { const response = await fetch(`${host}/index.html`); const body = await response.text(); - expect(response.status).to.equal(200); - expect(body).to.not.include(webSocketScript); + assert.equal(response.status, 200); + assert.ok(!body.includes(webSocketScript)); } finally { server.stop(); } @@ -77,10 +78,10 @@ describe('webSocketsPlugin', () => { const bodyA = await responseA.text(); const bodyB = await responseB.text(); - expect(responseA.status).to.equal(200); - expect(responseB.status).to.equal(200); - expect(bodyA).to.not.include(webSocketScript); - expect(bodyB).to.not.include(webSocketScript); + assert.equal(responseA.status, 200); + assert.equal(responseB.status, 200); + assert.ok(!bodyA.includes(webSocketScript)); + assert.ok(!bodyB.includes(webSocketScript)); } finally { server.stop(); } From 81945885c5d7dbf3106b162b50d02786a433f294 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Tue, 2 Jun 2026 23:33:33 +0300 Subject: [PATCH 2/3] chore(dev-server-core): add changeset for test-helpers breaking change Assisted-By: Claude Opus 4.6 (1M context) --- .changeset/brave-bags-march.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/brave-bags-march.md diff --git a/.changeset/brave-bags-march.md b/.changeset/brave-bags-march.md new file mode 100644 index 0000000000..79e13bae1c --- /dev/null +++ b/.changeset/brave-bags-march.md @@ -0,0 +1,5 @@ +--- +'@web/dev-server-core': major +--- + +Replace `chai` with `node:assert/strict` in the published `test-helpers` module. The `fetchText` helper now throws `node:assert`'s `AssertionError` instead of `chai`'s on non-200 responses. From 4bef3c14038d773a115b0e1615c9d9c754ebcef5 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Tue, 2 Jun 2026 23:50:35 +0300 Subject: [PATCH 3/3] style(dev-server-core): fix prettier formatting in test files Assisted-By: Claude Opus 4.6 (1M context) --- .../test/middleware/pluginServeMiddleware.test.ts | 5 +---- .../test/plugins/mimeTypesPlugin.test.ts | 10 ++-------- .../test/plugins/transformModuleImportsPlugin.test.ts | 8 ++------ 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts b/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts index e7a34a7bd3..686d7eac42 100644 --- a/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts +++ b/packages/dev-server-core/test/middleware/pluginServeMiddleware.test.ts @@ -82,10 +82,7 @@ describe('plugin-serve middleware', () => { assert.equal(response.status, 200); assert.ok(responseText.includes('serving non-existing.js')); - assert.equal( - response.headers.get('content-type'), - 'application/javascript; charset=utf-8', - ); + assert.equal(response.headers.get('content-type'), 'application/javascript; charset=utf-8'); } finally { server.stop(); } diff --git a/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts b/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts index 244faf0b0d..091f9c5e47 100644 --- a/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts +++ b/packages/dev-server-core/test/plugins/mimeTypesPlugin.test.ts @@ -24,10 +24,7 @@ describe('mimeTypesPLugin', () => { try { const response = await fetch(`${host}/foo.css`); assert.equal(response.status, 200); - assert.equal( - response.headers.get('content-type'), - 'application/javascript; charset=utf-8', - ); + assert.equal(response.headers.get('content-type'), 'application/javascript; charset=utf-8'); } finally { server.stop(); } @@ -54,10 +51,7 @@ describe('mimeTypesPLugin', () => { try { const responseA = await fetch(`${host}/foo.css`); assert.equal(responseA.status, 200); - assert.equal( - responseA.headers.get('content-type'), - 'application/javascript; charset=utf-8', - ); + assert.equal(responseA.headers.get('content-type'), 'application/javascript; charset=utf-8'); const responseB = await fetch(`${host}/x/foo.css`); assert.equal(responseB.status, 200); diff --git a/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts b/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts index f5b935f79c..e859616286 100644 --- a/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts +++ b/packages/dev-server-core/test/plugins/transformModuleImportsPlugin.test.ts @@ -420,9 +420,7 @@ describe('transformImport', () => { assert.equal(response.status, 200); assert.ok( - responseText.includes( - "import { message } from 'my-module?transformed-1&transformed-2';", - ), + responseText.includes("import { message } from 'my-module?transformed-1&transformed-2';"), ); assert.ok(responseText.includes('./src/local-module.js?transformed-1&transformed-2')); } finally { @@ -460,9 +458,7 @@ describe('transformImport', () => { assert.equal(response.status, 200); assert.ok( - responseText.includes( - "import { message } from 'my-module?transformed-1&transformed-2';", - ), + responseText.includes("import { message } from 'my-module?transformed-1&transformed-2';"), ); assert.ok(responseText.includes('./src/local-module.js?transformed-1&transformed-2')); } finally {