Skip to content

test_runner: support mocking json modules #58007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -2168,6 +2168,11 @@ test('spies on an object method', (t) => {
added:
- v22.3.0
- v20.18.0
changes:
- version:
- REPLACEME
pr-url: https://github.com/nodejs/node/pull/58007
description: Support JSON modules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
description: Support JSON modules.
description: Add support for JSON modules mocking.

-->

> Stability: 1.0 - Early development
Expand All @@ -2191,10 +2196,10 @@ added:
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.

This function is used to mock the exports of ECMAScript modules, CommonJS
modules, and Node.js builtin modules. Any references to the original module
prior to mocking are not impacted. In order to enable module mocking, Node.js must
be started with the [`--experimental-test-module-mocks`][] command-line flag.
This function is used to mock the exports of ECMAScript modules, CommonJS modules, JSON modules, and
Node.js builtin modules. Any references to the original module prior to mocking are not impacted. In
order to enable module mocking, Node.js must be started with the
[`--experimental-test-module-mocks`][] command-line flag.
Comment on lines +2199 to +2202
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only change here was to add JSON modules,.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's minimize the diff

Suggested change
This function is used to mock the exports of ECMAScript modules, CommonJS modules, JSON modules, and
Node.js builtin modules. Any references to the original module prior to mocking are not impacted. In
order to enable module mocking, Node.js must be started with the
[`--experimental-test-module-mocks`][] command-line flag.
This function is used to mock the exports of ECMAScript modules, CommonJS modules, JSON
modules, and Node.js builtin modules. Any references to the original module
prior to mocking are not impacted. In order to enable module mocking, Node.js must
be started with the [`--experimental-test-module-mocks`][] command-line flag.


The following example demonstrates how a mock is created for a module.

Expand Down
15 changes: 11 additions & 4 deletions lib/internal/test_runner/mock/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,17 @@ async function load(url, context, nextLoad) {
// Treat builtins as commonjs because customization hooks do not allow a
// core module to be replaced.
// Also collapse 'commonjs-sync' and 'require-commonjs' to 'commonjs'.
const format = (
original.format === 'builtin' ||
original.format === 'commonjs-sync' ||
original.format === 'require-commonjs') ? 'commonjs' : original.format;
let format = original.format;
switch (original.format) {
case 'builtin': // Deliberate fallthrough
case 'commonjs-sync': // Deliberate fallthrough
case 'require-commonjs':
format = 'commonjs';
break;
case 'json':
format = 'module';
Comment on lines +128 to +129
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main change. Creating a nested ternary seemed messy, so I changed this to a switch.

break;
}

const result = {
__proto__: null,
Expand Down
9 changes: 8 additions & 1 deletion lib/internal/test_runner/mock/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ 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', 'module-typescript', 'commonjs-typescript'];
const kSupportedFormats = [
'builtin',
'commonjs-typescript',
'commonjs',
'json',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding 'json', was the only real change here.

'module-typescript',
'module',
];
let sharedModuleState;

class MockFunctionContext {
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/module-mocking/basic.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could use an existing fixtures JSON file (e.g. test/fixtures/experimental.json), or at least move/rename it to test/fixtures/simple.json so it can be used by other tests

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"foo":"bar"}
28 changes: 28 additions & 0 deletions test/parallel/test-runner-module-mocking.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
await t.test('CJS', async (t) => {
const fixture = fixtures.path('module-mocking', 'basic-cjs.js');
Expand Down
Loading