| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e992a34 commit 2c63d30
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2480,16 +2480,32 @@ changes: | |||
| 2480 | 2480 | generates a new mock module. If `true`, subsequent calls will return the same | |
| 2481 | 2481 | module mock, and the mock module is inserted into the CommonJS cache. | |
| 2482 | 2482 | **Default:** false. | |
| 2483 | + * `exports` {Object} Optional mocked exports. The `default` property, if | ||
| 2484 | + provided, is used as the mocked module's default export. All other own | ||
| 2485 | + enumerable properties are used as named exports. | ||
| 2486 | + **This option cannot be used with `defaultExport` or `namedExports`.** | ||
| 2487 | + * If the mock is a CommonJS or builtin module, `exports.default` is used as | ||
| 2488 | + the value of `module.exports`. | ||
| 2489 | + * If `exports.default` is not provided for a CommonJS or builtin mock, | ||
| 2490 | + `module.exports` defaults to an empty object. | ||
| 2491 | + * If named exports are provided with a non-object default export, the mock | ||
| 2492 | + throws an exception when used as a CommonJS or builtin module. | ||
| 2483 | 2493 | * `defaultExport` {any} An optional value used as the mocked module's default | |
| 2484 | 2494 | export. If this value is not provided, ESM mocks do not include a default | |
| 2485 | 2495 | export. If the mock is a CommonJS or builtin module, this setting is used as | |
| 2486 | 2496 | the value of `module.exports`. If this value is not provided, CJS and builtin | |
| 2487 | 2497 | mocks use an empty object as the value of `module.exports`. | |
| 2498 | + **This option cannot be used with `options.exports`.** | ||
| 2499 | + This option is deprecated and will be removed in a later version. | ||
| 2500 | + Prefer `options.exports.default`. | ||
| 2488 | 2501 | * `namedExports` {Object} An optional object whose keys and values are used to | |
| 2489 | 2502 | create the named exports of the mock module. If the mock is a CommonJS or | |
| 2490 | 2503 | builtin module, these values are copied onto `module.exports`. Therefore, if a | |
| 2491 | 2504 | mock is created with both named exports and a non-object default export, the | |
| 2492 | 2505 | mock will throw an exception when used as a CJS or builtin module. | |
| 2506 | + **This option cannot be used with `options.exports`.** | ||
| 2507 | + This option is deprecated and will be removed in a later version. | ||
| 2508 | + Prefer `options.exports`. | ||
| 2493 | 2509 | * Returns: {MockModuleContext} An object that can be used to manipulate the mock. | |
| 2494 | 2510 | ||
| 2495 | 2511 | This function is used to mock the exports of ECMAScript modules, CommonJS modules, JSON modules, and | |
@@ -2506,10 +2522,10 @@ The following example demonstrates how a mock is created for a module. | |||
| 2506 | 2522 | ||
| 2507 | 2523 | ```js | |
| 2508 | 2524 | test('mocks a builtin module in both module systems', async (t) => { | |
| 2509 | - // Create a mock of 'node:readline' with a named export named 'fn', which | ||
| 2525 | + // Create a mock of 'node:readline' with a named export named 'foo', which | ||
| 2510 | 2526 | // does not exist in the original 'node:readline' module. | |
| 2511 | 2527 | const mock = t.mock.module('node:readline', { | |
| 2512 | - namedExports: { fn() { return 42; } }, | ||
| 2528 | + exports: { foo: () => 42 }, | ||
| 2513 | 2529 | }); | |
| 2514 | 2530 | ||
| 2515 | 2531 | let esmImpl = await import('node:readline'); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -113,10 +113,10 @@ function defaultExportSource(useESM, hasDefaultExport) { | |||
| 113 | 113 | if (!hasDefaultExport) { | |
| 114 | 114 | return ''; | |
| 115 | 115 | } else if (useESM) { | |
| 116 | - return 'export default $__exports.defaultExport;'; | ||
| 116 | + return 'export default $__exports.moduleExports.default;'; | ||
| 117 | 117 | } | |
| 118 | 118 | ||
| 119 | - return 'module.exports = $__exports.defaultExport;'; | ||
| 119 | + return 'module.exports = $__exports.moduleExports.default;'; | ||
| 120 | 120 | } | |
| 121 | 121 | ||
| 122 | 122 | function namedExportsSource(useESM, exportNames) { | |
@@ -134,9 +134,9 @@ if (module.exports === null || typeof module.exports !== 'object') { | |||
| 134 | 134 | const name = exportNames[i]; | |
| 135 | 135 | ||
| 136 | 136 | if (useESM) { | |
| 137 | - source += `export let ${name} = $__exports.namedExports[${JSONStringify(name)}];\n`; | ||
| 137 | + source += `export let ${name} = $__exports.moduleExports[${JSONStringify(name)}];\n`; | ||
| 138 | 138 | } else { | |
| 139 | - source += `module.exports[${JSONStringify(name)}] = $__exports.namedExports[${JSONStringify(name)}];\n`; | ||
| 139 | + source += `module.exports[${JSONStringify(name)}] = $__exports.moduleExports[${JSONStringify(name)}];\n`; | ||
| 140 | 140 | } | |
| 141 | 141 | } | |
| 142 | 142 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,12 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | const { | |
| 3 | + ArrayPrototypeFilter, | ||
| 3 | 4 | ArrayPrototypePush, | |
| 4 | 5 | ArrayPrototypeSlice, | |
| 5 | 6 | Error, | |
| 6 | 7 | FunctionPrototypeBind, | |
| 7 | 8 | FunctionPrototypeCall, | |
| 9 | + ObjectAssign, | ||
| 8 | 10 | ObjectDefineProperty, | |
| 9 | 11 | ObjectGetOwnPropertyDescriptor, | |
| 10 | 12 | ObjectGetPrototypeOf, | |
@@ -33,6 +35,7 @@ const { | |||
| 33 | 35 | URLParse, | |
| 34 | 36 | } = require('internal/url'); | |
| 35 | 37 | const { | |
| 38 | + deprecateProperty, | ||
| 36 | 39 | emitExperimentalWarning, | |
| 37 | 40 | getStructuredStack, | |
| 38 | 41 | kEmptyObject, | |
@@ -61,6 +64,14 @@ const kSupportedFormats = [ | |||
| 61 | 64 | 'module', | |
| 62 | 65 | ]; | |
| 63 | 66 | let sharedModuleState; | |
| 67 | + const deprecateNamedExports = deprecateProperty( | ||
| 68 | + 'namedExports', | ||
| 69 | + 'mock.module(): options.namedExports is deprecated. Use options.exports instead.', | ||
| 70 | + ); | ||
| 71 | + const deprecateDefaultExport = deprecateProperty( | ||
| 72 | + 'defaultExport', | ||
| 73 | + 'mock.module(): options.defaultExport is deprecated. Use options.exports.default instead.', | ||
| 74 | + ); | ||
| 64 | 75 | const { | |
| 65 | 76 | hooks: mockHooks, | |
| 66 | 77 | mocks, | |
@@ -185,20 +196,16 @@ class MockModuleContext { | |||
| 185 | 196 | baseURL, | |
| 186 | 197 | cache, | |
| 187 | 198 | caller, | |
| 188 | - defaultExport, | ||
| 189 | 199 | format, | |
| 190 | 200 | fullPath, | |
| 191 | - hasDefaultExport, | ||
| 192 | - namedExports, | ||
| 201 | + moduleExports, | ||
| 193 | 202 | sharedState, | |
| 194 | 203 | specifier, | |
| 195 | 204 | }) { | |
| 196 | 205 | const config = { | |
| 197 | 206 | __proto__: null, | |
| 198 | 207 | cache, | |
| 199 | - defaultExport, | ||
| 200 | - hasDefaultExport, | ||
| 201 | - namedExports, | ||
| 208 | + moduleExports, | ||
| 202 | 209 | caller, | |
| 203 | 210 | }; | |
| 204 | 211 | ||
@@ -230,8 +237,8 @@ class MockModuleContext { | |||
| 230 | 237 | __proto__: null, | |
| 231 | 238 | url: baseURL, | |
| 232 | 239 | cache, | |
| 233 | - exportNames: ObjectKeys(namedExports), | ||
| 234 | - hasDefaultExport, | ||
| 240 | + exportNames: ArrayPrototypeFilter(ObjectKeys(moduleExports), (k) => k !== 'default'), | ||
| 241 | + hasDefaultExport: 'default' in moduleExports, | ||
| 235 | 242 | format, | |
| 236 | 243 | localVersion, | |
| 237 | 244 | active: true, | |
@@ -241,8 +248,7 @@ class MockModuleContext { | |||
| 241 | 248 | delete Module._cache[fullPath]; | |
| 242 | 249 | sharedState.mockExports.set(baseURL, { | |
| 243 | 250 | __proto__: null, | |
| 244 | - defaultExport, | ||
| 245 | - namedExports, | ||
| 251 | + moduleExports, | ||
| 246 | 252 | }); | |
| 247 | 253 | } | |
| 248 | 254 | ||
@@ -627,14 +633,9 @@ class MockTracker { | |||
| 627 | 633 | debug('module mock entry, specifier = "%s", options = %o', specifier, options); | |
| 628 | 634 | ||
| 629 | 635 | const { | |
| 630 | - cache = false, | ||
| 631 | - namedExports = kEmptyObject, | ||
| 632 | - defaultExport, | ||
| 633 | - } = options; | ||
| 634 | - const hasDefaultExport = 'defaultExport' in options; | ||
| 635 | - | ||
| 636 | - validateBoolean(cache, 'options.cache'); | ||
| 637 | - validateObject(namedExports, 'options.namedExports'); | ||
| 636 | + cache, | ||
| 637 | + moduleExports, | ||
| 638 | + } = normalizeModuleMockOptions(options); | ||
| 638 | 639 | ||
| 639 | 640 | const sharedState = setupSharedModuleState(); | |
| 640 | 641 | const mockSpecifier = StringPrototypeStartsWith(specifier, 'node:') ? | |
@@ -673,11 +674,9 @@ class MockTracker { | |||
| 673 | 674 | baseURL: baseURL.href, | |
| 674 | 675 | cache, | |
| 675 | 676 | caller, | |
| 676 | - defaultExport, | ||
| 677 | 677 | format, | |
| 678 | 678 | fullPath, | |
| 679 | - hasDefaultExport, | ||
| 680 | - namedExports, | ||
| 679 | + moduleExports, | ||
| 681 | 680 | sharedState, | |
| 682 | 681 | specifier: mockSpecifier, | |
| 683 | 682 | }); | |
@@ -816,6 +815,73 @@ class MockTracker { | |||
| 816 | 815 | } | |
| 817 | 816 | } | |
| 818 | 817 | ||
| 818 | + function normalizeModuleMockOptions(options) { | ||
| 819 | + const { cache = false } = options; | ||
| 820 | + validateBoolean(cache, 'options.cache'); | ||
| 821 | + | ||
| 822 | + const hasExports = 'exports' in options; | ||
| 823 | + const hasNamedExports = 'namedExports' in options; | ||
| 824 | + const hasDefaultExport = 'defaultExport' in options; | ||
| 825 | + | ||
| 826 | + deprecateNamedExports(options); | ||
| 827 | + deprecateDefaultExport(options); | ||
| 828 | + | ||
| 829 | + const moduleExports = { __proto__: null }; | ||
| 830 | + | ||
| 831 | + if (hasExports) { | ||
| 832 | + validateObject(options.exports, 'options.exports'); | ||
| 833 | + } | ||
| 834 | + | ||
| 835 | + if (hasNamedExports) { | ||
| 836 | + validateObject(options.namedExports, 'options.namedExports'); | ||
| 837 | + } | ||
| 838 | + | ||
| 839 | + if (hasExports && (hasNamedExports || hasDefaultExport)) { | ||
| 840 | + let reason = "cannot be used with 'options.namedExports'"; | ||
| 841 | + | ||
| 842 | + if (hasDefaultExport) { | ||
| 843 | + reason = hasNamedExports ? | ||
| 844 | + "cannot be used with 'options.namedExports' or 'options.defaultExport'" : | ||
| 845 | + "cannot be used with 'options.defaultExport'"; | ||
| 846 | + } | ||
| 847 | + | ||
| 848 | + throw new ERR_INVALID_ARG_VALUE('options.exports', options.exports, reason); | ||
| 849 | + } | ||
| 850 | + | ||
| 851 | + if (hasExports) { | ||
| 852 | + copyOwnProperties(options.exports, moduleExports); | ||
| 853 | + } | ||
| 854 | + | ||
| 855 | + if (hasNamedExports) { | ||
| 856 | + copyOwnProperties(options.namedExports, moduleExports); | ||
| 857 | + } | ||
| 858 | + | ||
| 859 | + if (hasDefaultExport) { | ||
| 860 | + ObjectDefineProperty( | ||
| 861 | + moduleExports, | ||
| 862 | + 'default', | ||
| 863 | + ObjectAssign({ __proto__: null }, ObjectGetOwnPropertyDescriptor(options, 'defaultExport')), | ||
| 864 | + ); | ||
| 865 | + } | ||
| 866 | + | ||
| 867 | + return { | ||
| 868 | + __proto__: null, | ||
| 869 | + cache, | ||
| 870 | + moduleExports, | ||
| 871 | + }; | ||
| 872 | + } | ||
| 873 | + | ||
| 874 | + | ||
| 875 | + function copyOwnProperties(from, to) { | ||
| 876 | + const keys = ObjectKeys(from); | ||
| 877 | + | ||
| 878 | + for (let i = 0; i < keys.length; ++i) { | ||
| 879 | + const key = keys[i]; | ||
| 880 | + const descriptor = ObjectGetOwnPropertyDescriptor(from, key); | ||
| 881 | + ObjectDefineProperty(to, key, descriptor); | ||
| 882 | + } | ||
| 883 | + } | ||
| 884 | + | ||
| 819 | 885 | function setupSharedModuleState() { | |
| 820 | 886 | if (sharedModuleState === undefined) { | |
| 821 | 887 | const { mock } = require('test'); | |
@@ -855,9 +921,7 @@ function cjsMockModuleLoad(request, parent, isMain) { | |||
| 855 | 921 | const { | |
| 856 | 922 | cache, | |
| 857 | 923 | caller, | |
| 858 | - defaultExport, | ||
| 859 | - hasDefaultExport, | ||
| 860 | - namedExports, | ||
| 924 | + moduleExports, | ||
| 861 | 925 | } = config; | |
| 862 | 926 | ||
| 863 | 927 | if (cache && Module._cache[resolved]) { | |
@@ -866,9 +930,10 @@ function cjsMockModuleLoad(request, parent, isMain) { | |||
| 866 | 930 | return Module._cache[resolved].exports; | |
| 867 | 931 | } | |
| 868 | 932 | ||
| 933 | + const hasDefaultExport = 'default' in moduleExports; | ||
| 869 | 934 | // eslint-disable-next-line node-core/set-proto-to-null-in-object | |
| 870 | - const modExports = hasDefaultExport ? defaultExport : {}; | ||
| 871 | - const exportNames = ObjectKeys(namedExports); | ||
| 935 | + const modExports = hasDefaultExport ? moduleExports.default : {}; | ||
| 936 | + const exportNames = ArrayPrototypeFilter(ObjectKeys(moduleExports), (k) => k !== 'default'); | ||
| 872 | 937 | ||
| 873 | 938 | if ((typeof modExports !== 'object' || modExports === null) && | |
| 874 | 939 | exportNames.length > 0) { | |
@@ -878,7 +943,7 @@ function cjsMockModuleLoad(request, parent, isMain) { | |||
| 878 | 943 | ||
| 879 | 944 | for (let i = 0; i < exportNames.length; ++i) { | |
| 880 | 945 | const name = exportNames[i]; | |
| 881 | - const descriptor = ObjectGetOwnPropertyDescriptor(namedExports, name); | ||
| 946 | + const descriptor = ObjectGetOwnPropertyDescriptor(moduleExports, name); | ||
| 882 | 947 | ObjectDefineProperty(modExports, name, descriptor); | |
| 883 | 948 | } | |
| 884 | 949 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,7 @@ | |||
| 1 | 1 | import { mock, test } from 'node:test'; | |
| 2 | 2 | ||
| 3 | 3 | const dependency = mock.fn(() => 'mock-return-value'); | |
| 4 | - mock.module('../coverage-with-mock/dependency.cjs', { namedExports: { dependency } }); | ||
| 4 | + mock.module('../coverage-with-mock/dependency.cjs', { exports: { dependency } }); | ||
| 5 | 5 | ||
| 6 | 6 | const { subject } = await import('../coverage-with-mock/subject.mjs'); | |
| 7 | 7 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ import { describe, it, mock } from 'node:test'; | |||
| 2 | 2 | ||
| 3 | 3 | describe('module test with mock', async () => { | |
| 4 | 4 | mock.module('../coverage-with-mock/sum.js', { | |
| 5 | - namedExports: { | ||
| 5 | + exports: { | ||
| 6 | 6 | sum: (a, b) => 1, | |
| 7 | 7 | getData: () => ({}), | |
| 8 | 8 | }, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -10,8 +10,10 @@ describe('foo', { concurrency: true }, () => { | |||
| 10 | 10 | .then(({ default: _, ...rest }) => rest); | |
| 11 | 11 | ||
| 12 | 12 | mock.module('../coverage/bar.mts', { | |
| 13 | - defaultExport: barMock, | ||
| 14 | - namedExports: barNamedExports, | ||
| 13 | + exports: { | ||
| 14 | + ...barNamedExports, | ||
| 15 | + default: barMock, | ||
| 16 | + }, | ||
| 15 | 17 | }); | |
| 16 | 18 | ||
| 17 | 19 | ({ foo } = await import('../coverage/foo.mts')); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -34,6 +34,6 @@ ok 1 - foo | |||
| 34 | 34 | # output | | | | | |
| 35 | 35 | # typescript-coverage.mts | 100.00 | 100.00 | 100.00 | | |
| 36 | 36 | # ---------------------------------------------------------------------------- | |
| 37 | - # all files | 93.55 | 100.00 | 85.71 | | ||
| 37 | + # all files | 93.94 | 100.00 | 85.71 | | ||
| 38 | 38 | # ---------------------------------------------------------------------------- | |
| 39 | 39 | # end of coverage report | |
| Back | FazBrowse Home | New Git URL |
0 commit comments