test_runner: mock dual-package with conditional exports · nodejs/node@797ef40 · GitHub
Skip to content

Commit 797ef40

Browse files
maruthangaduh95
authored andcommitted
test_runner: mock dual-package with conditional exports
When `mock.module()` targets a package whose `exports` field maps `import` and `require` to different files, the ESM resolver and the CJS resolver disagree on the resolved path. Only the ESM path was registered in `mockMap`, so `require()` of the mocked specifier bypassed the mock and loaded the real CJS module. Resolve the specifier through `Module._resolveFilename` from the caller's directory in addition to the existing ESM resolution. When the two paths differ, register the CJS path as a second key in `mockMap` and invalidate `Module._cache[cjsPath]`, restoring it on `restore()`. Single-resolution packages keep their existing behavior. Fixes: #58231 Signed-off-by: Maruthan G <maruthang4@gmail.com> PR-URL: #62943 Reviewed-By: Aviv Keller <me@aviv.sh> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Jacob Smith <jacob@frende.me>
1 parent 1ad7ca3 commit 797ef40

6 files changed

Lines changed: 173 additions & 0 deletions

File tree

lib/internal/test_runner/mock/mock.js

Lines changed: 86 additions & 0 deletions
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
const assert = require('node:assert');
3+
const { test } = require('node:test');
4+
const fixture = 'dual-pkg-with-exports';
5+
6+
test('mock node_modules dual package with conditional exports', async (t) => {
7+
const mock = t.mock.module(fixture, {
8+
namedExports: { add(x, y) { return 1 + x + y; }, flavor: 'mocked' },
9+
});
10+
11+
// CJS require should pick up the mock even though the package's "exports"
12+
// field maps the "require" condition to a different file than "import".
13+
const cjsImpl = require(fixture);
14+
assert.strictEqual(cjsImpl.add(4, 5), 10);
15+
assert.strictEqual(cjsImpl.flavor, 'mocked');
16+
17+
// ESM dynamic import should also pick up the mock.
18+
const esmImpl = await import(fixture);
19+
assert.strictEqual(esmImpl.add(4, 5), 10);
20+
assert.strictEqual(esmImpl.flavor, 'mocked');
21+
22+
mock.restore();
23+
24+
// After restore, both module systems should see the original exports.
25+
const restoredCjs = require(fixture);
26+
assert.strictEqual(restoredCjs.add(4, 5), 9);
27+
assert.strictEqual(restoredCjs.flavor, 'cjs');
28+
29+
const restoredEsm = await import(fixture);
30+
assert.strictEqual(restoredEsm.add(4, 5), 9);
31+
assert.strictEqual(restoredEsm.flavor, 'esm');
32+
});

test/fixtures/test-runner/node_modules/dual-pkg-with-exports/index.cjs

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/test-runner/node_modules/dual-pkg-with-exports/index.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/test-runner/node_modules/dual-pkg-with-exports/package.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 36 additions & 0 deletions

0 commit comments

Comments
 (0)