From c2f4b0855c9e73e3cd4359cce2360a659c167c06 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 10 Sep 2024 18:01:39 +0200 Subject: [PATCH] test_runner: support typescript module mocking --- lib/internal/modules/esm/translators.js | 4 ++-- lib/internal/test_runner/mock/loader.js | 2 +- lib/internal/test_runner/mock/mock.js | 7 ++++-- test/es-module/test-typescript.mjs | 14 +++++++++++ .../fixtures/typescript/ts/commonjs-logger.ts | 5 ++++ test/fixtures/typescript/ts/module-logger.ts | 1 + .../typescript/ts/test-mock-module.ts | 24 +++++++++++++++++++ 7 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/typescript/ts/commonjs-logger.ts create mode 100644 test/fixtures/typescript/ts/module-logger.ts create mode 100644 test/fixtures/typescript/ts/test-mock-module.ts diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index b1e7b86095c37e..69c0b6df0a1810 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -478,7 +478,7 @@ translators.set('wasm', async function(url, source) { // Strategy for loading a commonjs TypeScript module translators.set('commonjs-typescript', function(url, source) { emitExperimentalWarning('Type Stripping'); - assertBufferSource(source, false, 'load'); + assertBufferSource(source, true, 'load'); const code = stripTypeScriptTypes(stringify(source), url); debug(`Translating TypeScript ${url}`); return FunctionPrototypeCall(translators.get('commonjs'), this, url, code, false); @@ -487,7 +487,7 @@ translators.set('commonjs-typescript', function(url, source) { // Strategy for loading an esm TypeScript module translators.set('module-typescript', function(url, source) { emitExperimentalWarning('Type Stripping'); - assertBufferSource(source, false, 'load'); + assertBufferSource(source, true, 'load'); const code = stripTypeScriptTypes(stringify(source), url); debug(`Translating TypeScript ${url}`); return FunctionPrototypeCall(translators.get('module'), this, url, code, false); diff --git a/lib/internal/test_runner/mock/loader.js b/lib/internal/test_runner/mock/loader.js index 25d9b31d0cb74d..29d1ef70ebf9fc 100644 --- a/lib/internal/test_runner/mock/loader.js +++ b/lib/internal/test_runner/mock/loader.js @@ -137,7 +137,7 @@ async function load(url, context, nextLoad) { async function createSourceFromMock(mock, format) { // Create mock implementation from provided exports. const { exportNames, hasDefaultExport, url } = mock; - const useESM = format === 'module'; + const useESM = format === 'module' || format === 'module-typescript'; const source = `${testImportSource(useESM)} if (!$__test.mock._mockExports.has('${url}')) { throw new Error(${JSONStringify(`mock exports not found for "${url}"`)}); diff --git a/lib/internal/test_runner/mock/mock.js b/lib/internal/test_runner/mock/mock.js index 5d3bce816aa69b..99fe1baebb771b 100644 --- a/lib/internal/test_runner/mock/mock.js +++ b/lib/internal/test_runner/mock/mock.js @@ -70,7 +70,7 @@ const kMockUnknownMessage = 3; const kWaitTimeout = 5_000; const kBadExportsMessage = 'Cannot create mock because named exports ' + 'cannot be applied to the provided default export.'; -const kSupportedFormats = ['builtin', 'commonjs', 'module']; +const kSupportedFormats = ['builtin', 'commonjs', 'module', 'module-typescript', 'commonjs-typescript']; let sharedModuleState; class MockFunctionContext { @@ -517,7 +517,10 @@ class MockTracker { // Get the file that called this function. We need four stack frames: // vm context -> getStructuredStack() -> this function -> actual caller. - const caller = pathToFileURL(getStructuredStack()[3]?.getFileName()).href; + const filename = getStructuredStack()[3]?.getFileName(); + // If the caller is already a file URL, use it as is. Otherwise, convert it. + const hasFileProtocol = StringPrototypeStartsWith(filename, 'file://'); + const caller = hasFileProtocol ? filename : pathToFileURL(filename).href; const { format, url } = sharedState.moduleLoader.resolveSync( mockSpecifier, caller, null, ); diff --git a/test/es-module/test-typescript.mjs b/test/es-module/test-typescript.mjs index 3fc753e56c8c8d..2bde803c80067d 100644 --- a/test/es-module/test-typescript.mjs +++ b/test/es-module/test-typescript.mjs @@ -324,3 +324,17 @@ test('execute a JavaScript file importing a cjs TypeScript file', async () => { match(result.stdout, /Hello, TypeScript!/); strictEqual(result.code, 0); }); + +test('execute a TypeScript test mocking module', async () => { + const result = await spawnPromisified(process.execPath, [ + '--test', + '--experimental-test-module-mocks', + '--experimental-strip-types', + '--no-warnings', + fixtures.path('typescript/ts/test-mock-module.ts'), + ]); + strictEqual(result.stderr, ''); + match(result.stdout, /Hello, TypeScript-Module!/); + match(result.stdout, /Hello, TypeScript-CommonJS!/); + strictEqual(result.code, 0); +}); diff --git a/test/fixtures/typescript/ts/commonjs-logger.ts b/test/fixtures/typescript/ts/commonjs-logger.ts new file mode 100644 index 00000000000000..91b171231e5522 --- /dev/null +++ b/test/fixtures/typescript/ts/commonjs-logger.ts @@ -0,0 +1,5 @@ +const logger = (): void => { }; + +module.exports = { + logger +} diff --git a/test/fixtures/typescript/ts/module-logger.ts b/test/fixtures/typescript/ts/module-logger.ts new file mode 100644 index 00000000000000..50aecfdf350f33 --- /dev/null +++ b/test/fixtures/typescript/ts/module-logger.ts @@ -0,0 +1 @@ +export const logger = (): void => { }; diff --git a/test/fixtures/typescript/ts/test-mock-module.ts b/test/fixtures/typescript/ts/test-mock-module.ts new file mode 100644 index 00000000000000..dc524815225a6c --- /dev/null +++ b/test/fixtures/typescript/ts/test-mock-module.ts @@ -0,0 +1,24 @@ +import { before, mock, test } from 'node:test'; +import { strictEqual } from 'node:assert'; + +const logger = mock.fn((s) => console.log(`Hello, ${s}!`)); + +before(async () => { + mock.module('./module-logger.ts', { + namedExports: { logger } + }); + mock.module('./commonjs-logger.ts', { + namedExports: { logger } + }); +}); + +test('logger', async () => { + + const { logger: mockedModule} = await import('./module-logger.ts'); + mockedModule('TypeScript-Module'); + strictEqual(logger.mock.callCount(), 1); + + const { logger: mockedCommonjs } = await import('./commonjs-logger.ts'); + mockedCommonjs('TypeScript-CommonJS'); + strictEqual(logger.mock.callCount(), 2); +});