Skip to content

Commit

Permalink
test_runner: refactor mock_loader
Browse files Browse the repository at this point in the history
  • Loading branch information
aduh95 committed Aug 6, 2024
1 parent 7327e44 commit 18f8ed2
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 75 deletions.
4 changes: 2 additions & 2 deletions lib/internal/test_runner/mock/mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const {
} = require('internal/errors');
const esmLoader = require('internal/modules/esm/loader');
const { getOptionValue } = require('internal/options');
const { fileURLToPath, toPathIfFileURL, URL } = require('internal/url');
const { fileURLToPath, toPathIfFileURL, URL, pathToFileURL } = require('internal/url');
const {
emitExperimentalWarning,
getStructuredStack,
Expand Down Expand Up @@ -508,7 +508,7 @@ class MockTracker {

// Get the file that called this function. We need four stack frames:
// vm context -> getStructuredStack() -> this function -> actual caller.
const caller = getStructuredStack()[3]?.getFileName();
const caller = pathToFileURL(getStructuredStack()[3]?.getFileName()).href;
const { format, url } = sharedState.moduleLoader.resolveSync(
mockSpecifier, caller, null,
);
Expand Down
48 changes: 8 additions & 40 deletions lib/test/mock_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,16 @@ const {
},
} = primordials;
const {
ensureNodeScheme,
kBadExportsMessage,
kMockSearchParam,
kMockSuccess,
kMockExists,
kMockUnknownMessage,
} = require('internal/test_runner/mock/mock');
const { pathToFileURL, URL } = require('internal/url');
const { normalizeReferrerURL } = require('internal/modules/helpers');
const { URL, URLParse } = require('internal/url');
let debug = require('internal/util/debuglog').debuglog('test_runner', (fn) => {
debug = fn;
});
const { createRequire, isBuiltin } = require('module');
const { defaultGetFormatWithoutErrors } = require('internal/modules/esm/get_format');
const { defaultResolve } = require('internal/modules/esm/resolve');

// TODO(cjihrig): This file should not be exposed publicly, but register() does
// not handle internal loaders. Before marking this API as stable, one of the
Expand Down Expand Up @@ -85,46 +80,18 @@ async function initialize(data) {

async function resolve(specifier, context, nextResolve) {
debug('resolve hook entry, specifier = "%s", context = %o', specifier, context);
let mockSpecifier;

if (isBuiltin(specifier)) {
mockSpecifier = ensureNodeScheme(specifier);
} else {
let format;

if (context.parentURL) {
format = defaultGetFormatWithoutErrors(pathToFileURL(context.parentURL));
}

try {
if (format === 'module') {
specifier = defaultResolve(specifier, context).url;
} else {
specifier = pathToFileURL(
createRequire(context.parentURL).resolve(specifier),
).href;
}
} catch {
const parentURL = normalizeReferrerURL(context.parentURL);
const parsedURL = URL.parse(specifier, parentURL)?.href;

if (parsedURL) {
specifier = parsedURL;
}
}

mockSpecifier = specifier;
}
const nextResolveResult = await nextResolve(specifier, context);
const mockSpecifier = nextResolveResult.url;

const mock = mocks.get(mockSpecifier);
debug('resolve hook, specifier = "%s", mock = %o', specifier, mock);

if (mock?.active !== true) {
return nextResolve(specifier, context);
return nextResolveResult;
}

const url = new URL(mockSpecifier);

url.searchParams.set(kMockSearchParam, mock.localVersion);

if (!mock.cache) {
Expand All @@ -133,13 +100,14 @@ async function resolve(specifier, context, nextResolve) {
mock.localVersion++;
}

debug('resolve hook finished, url = "%s"', url.href);
return nextResolve(url.href, context);
const { href } = url;
debug('resolve hook finished, url = "%s"', href);
return { __proto__: null, url: href, format: nextResolveResult.format };
}

async function load(url, context, nextLoad) {
debug('load hook entry, url = "%s", context = %o', url, context);
const parsedURL = URL.parse(url);
const parsedURL = URLParse(url);
if (parsedURL) {
parsedURL.searchParams.delete(kMockSearchParam);
}
Expand Down
44 changes: 11 additions & 33 deletions test/parallel/test-runner-module-mocking.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const fixtures = require('../common/fixtures');
const assert = require('node:assert');
const { relative } = require('node:path');
const { test } = require('node:test');
const { pathToFileURL } = require('node:url');
const { fileURLToPath, pathToFileURL } = require('node:url');

test('input validation', async (t) => {
await t.test('throws if specifier is not a string', (t) => {
Expand Down Expand Up @@ -514,41 +514,21 @@ test('CJS mocks can be used by both module systems', async (t) => {
const cjsMock = t.mock.module(cjsFixture, {
namedExports: { fn() { return 42; } },
});
let esmImpl = await import(cjsFixture);
let esmImpl = await import(pathToFileURL(cjsFixture));
let cjsImpl = require(cjsFixture);

assert.strictEqual(esmImpl.fn(), 42);
assert.strictEqual(cjsImpl.fn(), 42);

cjsMock.restore();

esmImpl = await import(cjsFixture);
esmImpl = await import(pathToFileURL(cjsFixture));
cjsImpl = require(cjsFixture);

assert.strictEqual(esmImpl.default.string, 'original cjs string');
assert.strictEqual(cjsImpl.string, 'original cjs string');
});

test('ESM mocks can be used by both module systems', async (t) => {
const esmFixture = fixtures.path('module-mocking', 'basic-esm.mjs');
const esmMock = t.mock.module(esmFixture, {
namedExports: { fn() { return 42; } },
});

let cjsImpl = require(esmFixture);
let esmImpl = await import(esmFixture);

assert.strictEqual(cjsImpl.fn(), 42);
assert.strictEqual(esmImpl.fn(), 42);

esmMock.restore();
cjsImpl = require(esmFixture);
esmImpl = await import(esmFixture);

assert.strictEqual(esmImpl.string, 'original esm string');
assert.strictEqual(cjsImpl.string, 'original esm string');
});

test('relative paths can be used by both module systems', async (t) => {
const fixture = relative(
__dirname, fixtures.path('module-mocking', 'basic-esm.mjs')
Expand Down Expand Up @@ -586,9 +566,7 @@ test('node_modules can be used by both module systems', async (t) => {
});

test('file:// imports are supported in ESM only', async (t) => {
const fixture = pathToFileURL(
fixtures.path('module-mocking', 'basic-esm.mjs')
).href;
const fixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs').href;
const mock = t.mock.module(fixture, {
namedExports: { fn() { return 42; } },
});
Expand All @@ -604,9 +582,9 @@ test('file:// imports are supported in ESM only', async (t) => {
});

test('mocked modules do not impact unmocked modules', async (t) => {
const mockedFixture = fixtures.path('module-mocking', 'basic-cjs.js');
const unmockedFixture = fixtures.path('module-mocking', 'basic-esm.mjs');
t.mock.module(mockedFixture, {
const mockedFixture = fixtures.fileURL('module-mocking', 'basic-cjs.js');
const unmockedFixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
t.mock.module(`${mockedFixture}`, {
namedExports: { fn() { return 42; } },
});
const mockedImpl = await import(mockedFixture);
Expand All @@ -625,18 +603,18 @@ test('defaultExports work with CJS mocks in both module systems', async (t) => {
assert.strictEqual(original.string, 'original cjs string');
t.mock.module(fixture, { defaultExport });
assert.strictEqual(require(fixture), defaultExport);
assert.strictEqual((await import(fixture)).default, defaultExport);
assert.strictEqual((await import(pathToFileURL(fixture))).default, defaultExport);
});

test('defaultExports work with ESM mocks in both module systems', async (t) => {
const fixture = fixtures.path('module-mocking', 'basic-esm.mjs');
const fixture = fixtures.fileURL('module-mocking', 'basic-esm.mjs');
const original = await import(fixture);
const defaultExport = Symbol('default');

assert.strictEqual(original.string, 'original esm string');
t.mock.module(fixture, { defaultExport });
t.mock.module(`${fixture}`, { defaultExport });
assert.strictEqual((await import(fixture)).default, defaultExport);
assert.strictEqual(require(fixture), defaultExport);
assert.strictEqual(require(fileURLToPath(fixture)), defaultExport);
});

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

0 comments on commit 18f8ed2

Please sign in to comment.