Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test_runner: add cwd option to run #54705

Merged
merged 20 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,9 @@ added:
- v18.9.0
- v16.19.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/54705
description: Added the `cwd` option.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/53937
description: Added coverage options.
Expand Down Expand Up @@ -1286,6 +1289,9 @@ changes:
parallel.
If `false`, it would only run one test file at a time.
**Default:** `false`.
* `cwd`: {string} Specifies the current working directory to be used by the test runner.
Serves as the base path for resolving files according to the [test runner execution model][].
**Default:** `process.cwd()`.
* `files`: {Array} An array containing the list of files to run.
**Default:** matching files from [test runner execution model][].
* `forceExit`: {boolean} Configures the test runner to exit the process once
Expand Down
5 changes: 2 additions & 3 deletions lib/internal/test_runner/coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,15 +487,14 @@ function sortCoverageFiles(a, b) {

function setupCoverage(options) {
let originalCoverageDirectory = process.env.NODE_V8_COVERAGE;
const cwd = process.cwd();

if (originalCoverageDirectory) {
// NODE_V8_COVERAGE was already specified. Convert it to an absolute path
// and store it for later. The test runner will use a temporary directory
// so that no preexisting coverage files interfere with the results of the
// coverage report. Then, once the coverage is computed, move the coverage
// files back to the original NODE_V8_COVERAGE directory.
originalCoverageDirectory = resolve(cwd, originalCoverageDirectory);
originalCoverageDirectory = resolve(options.cwd, originalCoverageDirectory);
}

const coverageDirectory = mkdtempSync(join(tmpdir(), 'node-coverage-'));
Expand All @@ -512,7 +511,7 @@ function setupCoverage(options) {
return new TestCoverage(
coverageDirectory,
originalCoverageDirectory,
cwd,
options.cwd,
options.coverageExcludeGlobs,
options.coverageIncludeGlobs,
{
Expand Down
3 changes: 2 additions & 1 deletion lib/internal/test_runner/harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function createProcessEventHandler(eventName, rootTest) {
const name = test.hookType ? `Test hook "${test.hookType}"` : `Test "${test.name}"`;
let locInfo = '';
if (test.loc) {
const relPath = relative(process.cwd(), test.loc.file);
const relPath = relative(rootTest.config.cwd, test.loc.file);
locInfo = ` at ${relPath}:${test.loc.line}:${test.loc.column}`;
}

Expand Down Expand Up @@ -260,6 +260,7 @@ function lazyBootstrapRoot() {
loc: entryFile ? [1, 1, entryFile] : undefined,
};
const globalOptions = parseCommandLine();
globalOptions.cwd = process.cwd();
createTestTree(rootTestOptions, globalOptions);
globalRoot.reporter.on('test:summary', (data) => {
if (!data.success) {
Expand Down
25 changes: 14 additions & 11 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const {

const { spawn } = require('child_process');
const { finished } = require('internal/streams/end-of-stream');
const { resolve } = require('path');
const { resolve, sep, isAbsolute } = require('path');
const { DefaultDeserializer, DefaultSerializer } = require('v8');
const { getOptionValue } = require('internal/options');
const { Interface } = require('internal/readline/interface');
Expand All @@ -56,14 +56,14 @@ const {
validateObject,
validateOneOf,
validateInteger,
validateString,
validateStringArray,
} = require('internal/validators');
const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
const { isRegExp } = require('internal/util/types');
const { pathToFileURL } = require('internal/url');
const {
createDeferredPromise,
getCWDURL,
kEmptyObject,
} = require('internal/util');
const { kEmitMessage } = require('internal/test_runner/tests_stream');
Expand Down Expand Up @@ -137,7 +137,8 @@ function getRunArgs(path, { forceExit,
testSkipPatterns,
only,
argv: suppliedArgs,
execArgv }) {
execArgv,
cwd }) {
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
if (forceExit === true) {
ArrayPrototypePush(argv, '--test-force-exit');
Expand Down Expand Up @@ -494,7 +495,8 @@ function watchFiles(testFiles, opts) {
// When file renamed (created / deleted) we need to update the watcher
if (newFileName) {
owners = new SafeSet().add(newFileName);
watcher.filterFile(resolve(newFileName), owners);
const resolveFileName = isAbsolute(newFileName) ? newFileName : resolve(opts.cwd, newFileName);
watcher.filterFile(resolveFileName, owners);
}

if (!newFileName && previousFileName) {
Expand Down Expand Up @@ -562,6 +564,7 @@ function run(options = kEmptyObject) {
functionCoverage = 0,
execArgv = [],
argv = [],
cwd = process.cwd(),
} = options;

if (files != null) {
Expand All @@ -586,6 +589,8 @@ function run(options = kEmptyObject) {
validateArray(globPatterns, 'options.globPatterns');
}

validateString(cwd, 'options.cwd');

if (globPatterns?.length > 0 && files?.length > 0) {
throw new ERR_INVALID_ARG_VALUE(
'options.globPatterns', globPatterns, 'is not supported when specifying \'options.files\'',
Expand Down Expand Up @@ -673,12 +678,9 @@ function run(options = kEmptyObject) {
lineCoverage: lineCoverage,
branchCoverage: branchCoverage,
functionCoverage: functionCoverage,
cwd,
};
const root = createTestTree(rootTestOptions, globalOptions);

// This const should be replaced by a run option in the future.
const cwd = process.cwd();

let testFiles = files ?? createTestFileList(globPatterns, cwd);

if (shard) {
Expand Down Expand Up @@ -731,7 +733,8 @@ function run(options = kEmptyObject) {
};
} else if (isolation === 'none') {
if (watch) {
filesWatcher = watchFiles(testFiles, opts);
const absoluteTestFiles = ArrayPrototypeMap(testFiles, (file) => (isAbsolute(file) ? file : resolve(cwd, file)));
filesWatcher = watchFiles(absoluteTestFiles, opts);
runFiles = async () => {
root.harness.bootstrapPromise = null;
root.harness.buildPromise = null;
Expand All @@ -744,7 +747,7 @@ function run(options = kEmptyObject) {
const { promise, resolve: finishBootstrap } = createDeferredPromise();

await root.runInAsyncScope(async () => {
const parentURL = getCWDURL().href;
const parentURL = pathToFileURL(cwd + sep).href;
const cascadedLoader = esmLoader.getOrInitializeCascadedLoader();
let topLevelTestCount = 0;

Expand All @@ -757,7 +760,7 @@ function run(options = kEmptyObject) {

for (let i = 0; i < testFiles.length; ++i) {
const testFile = testFiles[i];
const fileURL = pathToFileURL(testFile);
const fileURL = pathToFileURL(resolve(cwd, testFile));
const parent = i === 0 ? undefined : parentURL;
let threw = false;
let importError;
Expand Down
20 changes: 19 additions & 1 deletion test/fixtures/test-runner-watch.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,37 @@ const options = {
file: {
type: 'string',
},
cwd: {
type: 'string',
},
isolation: {
type: 'string',
},
};
const {
values,
positionals,
} = parseArgs({ args: process.argv.slice(2), options });

let files;
let cwd;
let isolation;

if (values.file) {
files = [values.file];
}

if (values.cwd) {
cwd = values.cwd;
}

if (values.isolation) {
isolation = values.isolation;
}

run({
files,
watch: true
watch: true,
cwd,
isolation,
}).compose(tap).pipe(process.stdout);
34 changes: 34 additions & 0 deletions test/parallel/test-runner-no-isolation-different-cwd.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { allowGlobals, mustCall } from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import { deepStrictEqual } from 'node:assert';
import { run } from 'node:test';

const stream = run({
cwd: fixtures.path('test-runner', 'no-isolation'),
isolation: 'none',
});


stream.on('test:pass', mustCall(4));
// eslint-disable-next-line no-unused-vars
for await (const _ of stream);
allowGlobals(globalThis.GLOBAL_ORDER);
deepStrictEqual(globalThis.GLOBAL_ORDER, [
'before one: <root>',
'suite one',
'before two: <root>',
'suite two',
'beforeEach one: suite one - test',
'beforeEach two: suite one - test',
'suite one - test',
'afterEach one: suite one - test',
'afterEach two: suite one - test',
'before suite two: suite two',
'beforeEach one: suite two - test',
'beforeEach two: suite two - test',
'suite two - test',
'afterEach one: suite two - test',
'afterEach two: suite two - test',
'after one: <root>',
'after two: <root>',
]);
Loading
Loading