Skip to content

Commit 68e94c1

Browse files
authored
test_runner: make mock.module's specifier consistent with import()
The previous implementation was trying to follow both `require` and `import` conventions. It is not practical to try to follow both, and aligning with `import()` seems to be what makes the most sense. PR-URL: nodejs#54416 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent 7c76fa0 commit 68e94c1

File tree

3 files changed

+39
-31
lines changed

3 files changed

+39
-31
lines changed

doc/api/test.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2050,7 +2050,7 @@ added: v22.3.0
20502050

20512051
> Stability: 1.0 - Early development
20522052
2053-
* `specifier` {string} A string identifying the module to mock.
2053+
* `specifier` {string|URL} A string identifying the module to mock.
20542054
* `options` {Object} Optional configuration options for the mock module. The
20552055
following properties are supported:
20562056
* `cache` {boolean} If `false`, each call to `require()` or `import()`

lib/internal/test_runner/mock/mock.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const {
3434
} = require('internal/errors');
3535
const esmLoader = require('internal/modules/esm/loader');
3636
const { getOptionValue } = require('internal/options');
37-
const { fileURLToPath, toPathIfFileURL, URL } = require('internal/url');
37+
const { fileURLToPath, toPathIfFileURL, URL, isURL } = require('internal/url');
3838
const {
3939
emitExperimentalWarning,
4040
getStructuredStack,
@@ -49,7 +49,6 @@ const {
4949
validateInteger,
5050
validateObject,
5151
validateOneOf,
52-
validateString,
5352
} = require('internal/validators');
5453
const { MockTimers } = require('internal/test_runner/mock/mock_timers');
5554
const { strictEqual, notStrictEqual } = require('assert');
@@ -488,7 +487,11 @@ class MockTracker {
488487

489488
module(specifier, options = kEmptyObject) {
490489
emitExperimentalWarning('Module mocking');
491-
validateString(specifier, 'specifier');
490+
if (typeof specifier !== 'string') {
491+
if (!isURL(specifier))
492+
throw new ERR_INVALID_ARG_TYPE('specifier', ['string', 'URL'], specifier);
493+
specifier = `${specifier}`;
494+
}
492495
validateObject(options, 'options');
493496
debug('module mock entry, specifier = "%s", options = %o', specifier, options);
494497

test/parallel/test-runner-module-mocking.js

+32-27
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const fixtures = require('../common/fixtures');
1010
const assert = require('node:assert');
1111
const { relative } = require('node:path');
1212
const { test } = require('node:test');
13-
const { fileURLToPath, pathToFileURL } = require('node:url');
13+
const { pathToFileURL } = require('node:url');
1414

1515
test('input validation', async (t) => {
1616
await t.test('throws if specifier is not a string', (t) => {
@@ -154,7 +154,7 @@ test('CJS mocking with namedExports option', async (t) => {
154154
assert.strictEqual(original.string, 'original cjs string');
155155
assert.strictEqual(original.fn, undefined);
156156

157-
t.mock.module(fixture, {
157+
t.mock.module(pathToFileURL(fixture), {
158158
namedExports: { fn() { return 42; } },
159159
});
160160
const mocked = require(fixture);
@@ -174,7 +174,7 @@ test('CJS mocking with namedExports option', async (t) => {
174174
assert.strictEqual(original.string, 'original cjs string');
175175
assert.strictEqual(original.fn, undefined);
176176

177-
t.mock.module(fixture, {
177+
t.mock.module(pathToFileURL(fixture), {
178178
namedExports: { fn() { return 42; } },
179179
cache: true,
180180
});
@@ -195,7 +195,7 @@ test('CJS mocking with namedExports option', async (t) => {
195195
assert.strictEqual(original.string, 'original cjs string');
196196
assert.strictEqual(original.fn, undefined);
197197

198-
t.mock.module(fixture, {
198+
t.mock.module(pathToFileURL(fixture), {
199199
namedExports: { fn() { return 42; } },
200200
cache: false,
201201
});
@@ -219,7 +219,7 @@ test('CJS mocking with namedExports option', async (t) => {
219219

220220
const defaultExport = { val1: 5, val2: 3 };
221221

222-
t.mock.module(fixture, {
222+
t.mock.module(pathToFileURL(fixture), {
223223
defaultExport,
224224
namedExports: { val1: 'mock value' },
225225
});
@@ -242,7 +242,7 @@ test('CJS mocking with namedExports option', async (t) => {
242242

243243
const defaultExport = null;
244244

245-
t.mock.module(fixture, {
245+
t.mock.module(pathToFileURL(fixture), {
246246
defaultExport,
247247
namedExports: { val1: 'mock value' },
248248
});
@@ -256,7 +256,7 @@ test('CJS mocking with namedExports option', async (t) => {
256256

257257
test('ESM mocking with namedExports option', async (t) => {
258258
await t.test('does not cache by default', async (t) => {
259-
const fixture = fixtures.path('module-mocking', 'basic-esm.mjs');
259+
const fixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
260260
const original = await import(fixture);
261261

262262
assert.strictEqual(original.string, 'original esm string');
@@ -276,7 +276,7 @@ test('ESM mocking with namedExports option', async (t) => {
276276
});
277277

278278
await t.test('explicitly enables caching', async (t) => {
279-
const fixture = fixtures.path('module-mocking', 'basic-esm.mjs');
279+
const fixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
280280
const original = await import(fixture);
281281

282282
assert.strictEqual(original.string, 'original esm string');
@@ -297,7 +297,7 @@ test('ESM mocking with namedExports option', async (t) => {
297297
});
298298

299299
await t.test('explicitly disables caching', async (t) => {
300-
const fixture = fixtures.path('module-mocking', 'basic-esm.mjs');
300+
const fixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
301301
const original = await import(fixture);
302302

303303
assert.strictEqual(original.string, 'original esm string');
@@ -318,7 +318,8 @@ test('ESM mocking with namedExports option', async (t) => {
318318
});
319319

320320
await t.test('named exports are not applied to defaultExport', async (t) => {
321-
const fixture = fixtures.path('module-mocking', 'basic-esm.mjs');
321+
const fixturePath = fixtures.path('module-mocking', 'basic-esm.mjs');
322+
const fixture = pathToFileURL(fixturePath);
322323
const original = await import(fixture);
323324

324325
assert.strictEqual(original.string, 'original esm string');
@@ -338,11 +339,11 @@ test('ESM mocking with namedExports option', async (t) => {
338339
assert.strictEqual(mocked.default, 'mock default');
339340
assert.strictEqual(mocked.val1, 'mock value');
340341
t.mock.reset();
341-
common.expectRequiredModule(require(fixture), original);
342+
common.expectRequiredModule(require(fixturePath), original);
342343
});
343344

344345
await t.test('throws if named exports cannot be applied to defaultExport as CJS', async (t) => {
345-
const fixture = fixtures.path('module-mocking', 'basic-cjs.js');
346+
const fixture = fixtures.fileURL('module-mocking', 'basic-cjs.js');
346347
const original = await import(fixture);
347348

348349
assert.strictEqual(original.default.string, 'original cjs string');
@@ -366,13 +367,14 @@ test('ESM mocking with namedExports option', async (t) => {
366367
test('modules cannot be mocked multiple times at once', async (t) => {
367368
await t.test('CJS', async (t) => {
368369
const fixture = fixtures.path('module-mocking', 'basic-cjs.js');
370+
const fixtureURL = pathToFileURL(fixture).href;
369371

370-
t.mock.module(fixture, {
372+
t.mock.module(fixtureURL, {
371373
namedExports: { fn() { return 42; } },
372374
});
373375

374376
assert.throws(() => {
375-
t.mock.module(fixture, {
377+
t.mock.module(fixtureURL, {
376378
namedExports: { fn() { return 55; } },
377379
});
378380
}, {
@@ -386,7 +388,7 @@ test('modules cannot be mocked multiple times at once', async (t) => {
386388
});
387389

388390
await t.test('ESM', async (t) => {
389-
const fixture = fixtures.path('module-mocking', 'basic-esm.mjs');
391+
const fixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs').href;
390392

391393
t.mock.module(fixture, {
392394
namedExports: { fn() { return 42; } },
@@ -409,10 +411,10 @@ test('modules cannot be mocked multiple times at once', async (t) => {
409411

410412
test('mocks are automatically restored', async (t) => {
411413
const cjsFixture = fixtures.path('module-mocking', 'basic-cjs.js');
412-
const esmFixture = fixtures.path('module-mocking', 'basic-esm.mjs');
414+
const esmFixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
413415

414416
await t.test('CJS', async (t) => {
415-
t.mock.module(cjsFixture, {
417+
t.mock.module(pathToFileURL(cjsFixture), {
416418
namedExports: { fn() { return 42; } },
417419
});
418420

@@ -442,9 +444,9 @@ test('mocks are automatically restored', async (t) => {
442444

443445
test('mocks can be restored independently', async (t) => {
444446
const cjsFixture = fixtures.path('module-mocking', 'basic-cjs.js');
445-
const esmFixture = fixtures.path('module-mocking', 'basic-esm.mjs');
447+
const esmFixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
446448

447-
const cjsMock = t.mock.module(cjsFixture, {
449+
const cjsMock = t.mock.module(pathToFileURL(cjsFixture), {
448450
namedExports: { fn() { return 42; } },
449451
});
450452

@@ -511,18 +513,19 @@ test('node:- core module mocks can be used by both module systems', async (t) =>
511513

512514
test('CJS mocks can be used by both module systems', async (t) => {
513515
const cjsFixture = fixtures.path('module-mocking', 'basic-cjs.js');
514-
const cjsMock = t.mock.module(cjsFixture, {
516+
const cjsFixtureURL = pathToFileURL(cjsFixture);
517+
const cjsMock = t.mock.module(cjsFixtureURL, {
515518
namedExports: { fn() { return 42; } },
516519
});
517-
let esmImpl = await import(pathToFileURL(cjsFixture));
520+
let esmImpl = await import(cjsFixtureURL);
518521
let cjsImpl = require(cjsFixture);
519522

520523
assert.strictEqual(esmImpl.fn(), 42);
521524
assert.strictEqual(cjsImpl.fn(), 42);
522525

523526
cjsMock.restore();
524527

525-
esmImpl = await import(pathToFileURL(cjsFixture));
528+
esmImpl = await import(cjsFixtureURL);
526529
cjsImpl = require(cjsFixture);
527530

528531
assert.strictEqual(esmImpl.default.string, 'original cjs string');
@@ -532,7 +535,7 @@ test('CJS mocks can be used by both module systems', async (t) => {
532535
test('relative paths can be used by both module systems', async (t) => {
533536
const fixture = relative(
534537
__dirname, fixtures.path('module-mocking', 'basic-esm.mjs')
535-
);
538+
).replaceAll('\\', '/');
536539
const mock = t.mock.module(fixture, {
537540
namedExports: { fn() { return 42; } },
538541
});
@@ -597,24 +600,26 @@ test('mocked modules do not impact unmocked modules', async (t) => {
597600

598601
test('defaultExports work with CJS mocks in both module systems', async (t) => {
599602
const fixture = fixtures.path('module-mocking', 'basic-cjs.js');
603+
const fixtureURL = pathToFileURL(fixture);
600604
const original = require(fixture);
601605
const defaultExport = Symbol('default');
602606

603607
assert.strictEqual(original.string, 'original cjs string');
604-
t.mock.module(fixture, { defaultExport });
608+
t.mock.module(fixtureURL, { defaultExport });
605609
assert.strictEqual(require(fixture), defaultExport);
606-
assert.strictEqual((await import(pathToFileURL(fixture))).default, defaultExport);
610+
assert.strictEqual((await import(fixtureURL)).default, defaultExport);
607611
});
608612

609613
test('defaultExports work with ESM mocks in both module systems', async (t) => {
610-
const fixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
614+
const fixturePath = fixtures.path('module-mocking', 'basic-esm.mjs');
615+
const fixture = pathToFileURL(fixturePath);
611616
const original = await import(fixture);
612617
const defaultExport = Symbol('default');
613618

614619
assert.strictEqual(original.string, 'original esm string');
615620
t.mock.module(`${fixture}`, { defaultExport });
616621
assert.strictEqual((await import(fixture)).default, defaultExport);
617-
assert.strictEqual(require(fileURLToPath(fixture)), defaultExport);
622+
assert.strictEqual(require(fixturePath), defaultExport);
618623
});
619624

620625
test('wrong import syntax should throw error after module mocking.', async () => {

0 commit comments

Comments
 (0)