Skip to content

Commit f5f21d3

Browse files
Han5991aduh95
authored andcommitted
test_runner: add exports option for module mocks
Add options.exports support in mock.module() and normalize option shapes through a shared exports path. Keep defaultExport and namedExports as aliases, emit runtime deprecation warnings for legacy options, and update docs and tests, including output fixtures and coverage snapshots. Refs: #58443 PR-URL: #61727 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
1 parent b15ea64 commit f5f21d3

8 files changed

Lines changed: 256 additions & 60 deletions

File tree

doc/api/test.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2477,16 +2477,32 @@ changes:
24772477
generates a new mock module. If `true`, subsequent calls will return the same
24782478
module mock, and the mock module is inserted into the CommonJS cache.
24792479
**Default:** false.
2480+
* `exports` {Object} Optional mocked exports. The `default` property, if
2481+
provided, is used as the mocked module's default export. All other own
2482+
enumerable properties are used as named exports.
2483+
**This option cannot be used with `defaultExport` or `namedExports`.**
2484+
* If the mock is a CommonJS or builtin module, `exports.default` is used as
2485+
the value of `module.exports`.
2486+
* If `exports.default` is not provided for a CommonJS or builtin mock,
2487+
`module.exports` defaults to an empty object.
2488+
* If named exports are provided with a non-object default export, the mock
2489+
throws an exception when used as a CommonJS or builtin module.
24802490
* `defaultExport` {any} An optional value used as the mocked module's default
24812491
export. If this value is not provided, ESM mocks do not include a default
24822492
export. If the mock is a CommonJS or builtin module, this setting is used as
24832493
the value of `module.exports`. If this value is not provided, CJS and builtin
24842494
mocks use an empty object as the value of `module.exports`.
2495+
**This option cannot be used with `options.exports`.**
2496+
This option is deprecated and will be removed in a later version.
2497+
Prefer `options.exports.default`.
24852498
* `namedExports` {Object} An optional object whose keys and values are used to
24862499
create the named exports of the mock module. If the mock is a CommonJS or
24872500
builtin module, these values are copied onto `module.exports`. Therefore, if a
24882501
mock is created with both named exports and a non-object default export, the
24892502
mock will throw an exception when used as a CJS or builtin module.
2503+
**This option cannot be used with `options.exports`.**
2504+
This option is deprecated and will be removed in a later version.
2505+
Prefer `options.exports`.
24902506
* Returns: {MockModuleContext} An object that can be used to manipulate the mock.
24912507

24922508
This function is used to mock the exports of ECMAScript modules, CommonJS modules, JSON modules, and
@@ -2503,10 +2519,10 @@ The following example demonstrates how a mock is created for a module.
25032519

25042520
```js
25052521
test('mocks a builtin module in both module systems', async (t) => {
2506-
// Create a mock of 'node:readline' with a named export named 'fn', which
2522+
// Create a mock of 'node:readline' with a named export named 'foo', which
25072523
// does not exist in the original 'node:readline' module.
25082524
const mock = t.mock.module('node:readline', {
2509-
namedExports: { fn() { return 42; } },
2525+
exports: { foo: () => 42 },
25102526
});
25112527

25122528
let esmImpl = await import('node:readline');

lib/internal/test_runner/mock/loader.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ function defaultExportSource(useESM, hasDefaultExport) {
113113
if (!hasDefaultExport) {
114114
return '';
115115
} else if (useESM) {
116-
return 'export default $__exports.defaultExport;';
116+
return 'export default $__exports.moduleExports.default;';
117117
}
118118

119-
return 'module.exports = $__exports.defaultExport;';
119+
return 'module.exports = $__exports.moduleExports.default;';
120120
}
121121

122122
function namedExportsSource(useESM, exportNames) {
@@ -134,9 +134,9 @@ if (module.exports === null || typeof module.exports !== 'object') {
134134
const name = exportNames[i];
135135

136136
if (useESM) {
137-
source += `export let ${name} = $__exports.namedExports[${JSONStringify(name)}];\n`;
137+
source += `export let ${name} = $__exports.moduleExports[${JSONStringify(name)}];\n`;
138138
} else {
139-
source += `module.exports[${JSONStringify(name)}] = $__exports.namedExports[${JSONStringify(name)}];\n`;
139+
source += `module.exports[${JSONStringify(name)}] = $__exports.moduleExports[${JSONStringify(name)}];\n`;
140140
}
141141
}
142142

lib/internal/test_runner/mock/mock.js

Lines changed: 92 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
'use strict';
22
const