Skip to content

Commit

Permalink
test_runner: add cwd option to run
Browse files Browse the repository at this point in the history
  • Loading branch information
pmarchini committed Sep 1, 2024
1 parent 3d954dc commit 4fa3784
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 4 deletions.
6 changes: 6 additions & 0 deletions doc/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,9 @@ added:
- v18.9.0
- v16.19.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/54225

Check warning on line 1250 in doc/api/test.md

View workflow job for this annotation

GitHub Actions / lint-pr-url

pr-url doesn't match the URL of the current PR.
description: Added the `cwd` option.
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/53927
description: Added the `isolation` option.
Expand Down Expand Up @@ -1273,6 +1276,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.
The cwd 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
9 changes: 5 additions & 4 deletions lib/internal/test_runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const {
validateObject,
validateOneOf,
validateInteger,
validateString,
} = require('internal/validators');
const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
const { isRegExp } = require('internal/util/types');
Expand Down Expand Up @@ -536,6 +537,7 @@ function run(options = kEmptyObject) {
setup,
only,
globPatterns,
cwd = process.cwd(),
} = options;

if (files != null) {
Expand All @@ -560,6 +562,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 @@ -625,9 +629,6 @@ function run(options = kEmptyObject) {
};
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 @@ -698,7 +699,7 @@ function run(options = kEmptyObject) {
root.harness.bootstrapPromise = promise;

for (let i = 0; i < testFiles.length; ++i) {
const testFile = testFiles[i];
const testFile = resolve(cwd, testFiles[i]);
const fileURL = pathToFileURL(testFile);
const parent = i === 0 ? undefined : parentURL;
let threw = false;
Expand Down
57 changes: 57 additions & 0 deletions test/parallel/test-runner-no-isolation-different-cwd-watch.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { allowGlobals, mustCall, mustNotCall } from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import { deepStrictEqual } from 'node:assert';
import { run } from 'node:test';

try {
const controller = new AbortController();
const stream = run({
cwd: fixtures.path('test-runner', 'no-isolation'),
isolation: 'none',
watch: true,
signal: controller.signal,
});

stream.on('test:fail', () => mustNotCall());
stream.on('test:pass', mustCall(5));
stream.on('data', function({ type }) {
if (type === 'test:watch:drained') {
controller.abort();
}
});
// 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',

'beforeEach one: test one',
'beforeEach two: test one',
'test one',
'afterEach one: test one',
'afterEach two: test one',

'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>',
]);
} catch (err) {
console.error(err);
process.exit(1);
}
48 changes: 48 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,48 @@
import { allowGlobals, mustCall } from '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import { strictEqual } from 'node:assert';
import { run } from 'node:test';

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

let errors = 0;
stream.on('test:fail', () => {
errors++;
});
stream.on('test:pass', mustCall(5));
// eslint-disable-next-line no-unused-vars
for await (const _ of stream);
strictEqual(errors, 0);
allowGlobals(globalThis.GLOBAL_ORDER);
strictEqual(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',

'beforeEach one: test one',
'beforeEach two: test one',
'test one',
'afterEach one: test one',
'afterEach two: test one',

'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>',
]);
34 changes: 34 additions & 0 deletions test/parallel/test-runner-run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,13 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
});
});

it('should only allow a string in options.cwd', async () => {
[Symbol(), {}, [], () => {}, 0, 1, 0n, 1n, true, false]
.forEach((cwd) => assert.throws(() => run({ cwd }), {
code: 'ERR_INVALID_ARG_TYPE'
}));
});

it('should only allow object as options', () => {
[Symbol(), [], () => {}, 0, 1, 0n, 1n, '', '1', true, false]
.forEach((options) => assert.throws(() => run(options), {
Expand Down Expand Up @@ -513,6 +520,33 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
for await (const _ of stream);
assert.match(stderr, /Warning: node:test run\(\) is being called recursively/);
});

it('should run with different cwd', async () => {
const stream = run({
cwd: fixtures.path('test-runner', 'cwd')
});
stream.on('test:fail', common.mustNotCall());
stream.on('test:pass', common.mustCall(1));

// eslint-disable-next-line no-unused-vars
for await (const _ of stream);
});

it('should run with different cwd while in watch mode', async () => {
const controller = new AbortController();
const stream = run({
cwd: fixtures.path('test-runner', 'cwd'),
watch: true,
signal: controller.signal,
}).on('data', function({ type }) {
if (type === 'test:watch:drained') {
controller.abort();
}
});

stream.on('test:fail', common.mustNotCall());
stream.on('test:pass', common.mustCall(1));
});
});

describe('forceExit', () => {
Expand Down

0 comments on commit 4fa3784

Please sign in to comment.