test_runner: support mocking json modules

PR-URL: https://github.com/nodejs/node/pull/58007
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
Jacob Smith 2025-04-26 16:56:24 +02:00 committed by Antoine du Hamel
parent ffe7e1ace0
commit 1bd7a2edf9
No known key found for this signature in database
GPG Key ID: 21D900FFDB233756
5 changed files with 57 additions and 9 deletions

View File

@ -2108,6 +2108,11 @@ test('spies on an object method', (t) => {
<!-- YAML <!-- YAML
added: v22.3.0 added: v22.3.0
changes:
- version:
- REPLACEME
pr-url: https://github.com/nodejs/node/pull/58007
description: Support JSON modules.
--> -->
> Stability: 1.0 - Early development > Stability: 1.0 - Early development
@ -2131,10 +2136,10 @@ added: v22.3.0
mock will throw an exception when used as a CJS or builtin module. mock will throw an exception when used as a CJS or builtin module.
* Returns: {MockModuleContext} An object that can be used to manipulate the mock. * Returns: {MockModuleContext} An object that can be used to manipulate the mock.
This function is used to mock the exports of ECMAScript modules, CommonJS This function is used to mock the exports of ECMAScript modules, CommonJS modules, JSON modules, and
modules, and Node.js builtin modules. Any references to the original module Node.js builtin modules. Any references to the original module prior to mocking are not impacted. In
prior to mocking are not impacted. In order to enable module mocking, Node.js must order to enable module mocking, Node.js must be started with the
be started with the [`--experimental-test-module-mocks`][] command-line flag. [`--experimental-test-module-mocks`][] command-line flag.
The following example demonstrates how a mock is created for a module. The following example demonstrates how a mock is created for a module.

View File

@ -118,10 +118,17 @@ async function load(url, context, nextLoad) {
// Treat builtins as commonjs because customization hooks do not allow a // Treat builtins as commonjs because customization hooks do not allow a
// core module to be replaced. // core module to be replaced.
// Also collapse 'commonjs-sync' and 'require-commonjs' to 'commonjs'. // Also collapse 'commonjs-sync' and 'require-commonjs' to 'commonjs'.
const format = ( let format = original.format;
original.format === 'builtin' || switch (original.format) {
original.format === 'commonjs-sync' || case 'builtin': // Deliberate fallthrough
original.format === 'require-commonjs') ? 'commonjs' : original.format; case 'commonjs-sync': // Deliberate fallthrough
case 'require-commonjs':
format = 'commonjs';
break;
case 'json':
format = 'module';
break;
}
const result = { const result = {
__proto__: null, __proto__: null,

View File

@ -70,7 +70,14 @@ const kMockUnknownMessage = 3;
const kWaitTimeout = 5_000; const kWaitTimeout = 5_000;
const kBadExportsMessage = 'Cannot create mock because named exports ' + const kBadExportsMessage = 'Cannot create mock because named exports ' +
'cannot be applied to the provided default export.'; 'cannot be applied to the provided default export.';
const kSupportedFormats = ['builtin', 'commonjs', 'module', 'module-typescript', 'commonjs-typescript']; const kSupportedFormats = [
'builtin',
'commonjs-typescript',
'commonjs',
'json',
'module-typescript',
'module',
];
let sharedModuleState; let sharedModuleState;
class MockFunctionContext { class MockFunctionContext {

View File

@ -0,0 +1 @@
{"foo":"bar"}

View File

@ -365,6 +365,34 @@ test('ESM mocking with namedExports option', async (t) => {
}); });
}); });
test('JSON mocking', async (t) => {
await t.test('with defaultExport', async (t) => {
const fixturePath = fixtures.path('module-mocking', 'basic.json');
const fixture = pathToFileURL(fixturePath);
const { default: original } = await import(fixture, { with: { type: 'json' } });
assert.deepStrictEqual(original, { foo: 'bar' });
const defaultExport = { qux: 'zed' };
t.mock.module(fixture, { defaultExport });
const { default: mocked } = await import(fixture, { with: { type: 'json' } });
assert.deepStrictEqual(mocked, defaultExport);
});
await t.test('throws without appropriate import attributes', async (t) => {
const fixturePath = fixtures.path('module-mocking', 'basic.json');
const fixture = pathToFileURL(fixturePath);
const defaultExport = { qux: 'zed' };
t.mock.module(fixture, { defaultExport });
await assert.rejects(() => import(fixture), /import attribute/);
});
});
test('modules cannot be mocked multiple times at once', async (t) => { test('modules cannot be mocked multiple times at once', async (t) => {
await t.test('CJS', async (t) => { await t.test('CJS', async (t) => {
const fixture = fixtures.path('module-mocking', 'basic-cjs.js'); const fixture = fixtures.path('module-mocking', 'basic-cjs.js');