Skip to content

Commit 170cd84

Browse files
committed
test_runner: support typescript in run
1 parent bdddc04 commit 170cd84

File tree

8 files changed

+140
-19
lines changed

8 files changed

+140
-19
lines changed

doc/api/test.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,9 @@ added:
12461246
- v18.9.0
12471247
- v16.19.0
12481248
changes:
1249+
- version: REPLACEME
1250+
pr-url: https://github.com/nodejs/node/pull/55126
1251+
description: Added typescript options.
12491252
- version: REPLACEME
12501253
pr-url: https://github.com/nodejs/node/pull/53937
12511254
description: Added coverage options.
@@ -1316,6 +1319,11 @@ changes:
13161319
fail after.
13171320
If unspecified, subtests inherit this value from their parent.
13181321
**Default:** `Infinity`.
1322+
* `typescript` {string} Configures the type of typescript support, if any. If set to `'transform'`,
1323+
each test file is run with [`--experimental-transform-types`][]. If set to `'strip'`,
1324+
each test file is run with [`--experimental-strip-types`][]. If set to `'none'`, type-stripping
1325+
does not occur.
1326+
**Default:** `'none'`.
13191327
* `watch` {boolean} Whether to run in watch mode or not. **Default:** `false`.
13201328
* `shard` {Object} Running tests in a specific shard. **Default:** `undefined`.
13211329
* `index` {number} is a positive integer between 1 and `<total>`
@@ -3562,9 +3570,11 @@ added:
35623570
Can be used to abort test subtasks when the test has been aborted.
35633571

35643572
[TAP]: https://testanything.org/
3573+
[`--experimental-strip-types`]: cli.md#--experimental-strip-types
35653574
[`--experimental-test-coverage`]: cli.md#--experimental-test-coverage
35663575
[`--experimental-test-module-mocks`]: cli.md#--experimental-test-module-mocks
35673576
[`--experimental-test-snapshots`]: cli.md#--experimental-test-snapshots
3577+
[`--experimental-transform-types`]: cli.md#--experimental-transform-types
35683578
[`--import`]: cli.md#--importmodule
35693579
[`--test-concurrency`]: cli.md#--test-concurrency
35703580
[`--test-coverage-include`]: cli.md#--test-coverage-include

lib/internal/test_runner/runner.js

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const {
8282
const {
8383
convertStringToRegExp,
8484
countCompletedTest,
85-
kDefaultPattern,
85+
getDefaultPattern,
8686
parseCommandLine,
8787
} = require('internal/test_runner/utils');
8888
const { Glob } = require('internal/fs/glob');
@@ -105,20 +105,20 @@ const kCanceledTests = new SafeSet()
105105

106106
let kResistStopPropagation;
107107

108-
function createTestFileList(patterns, cwd) {
109-
const hasUserSuppliedPattern = patterns != null;
110-
if (!patterns || patterns.length === 0) {
111-
patterns = [kDefaultPattern];
108+
function createTestFileList({ globPatterns, cwd, typescript }) {
109+
const hasUserSuppliedPattern = globPatterns != null;
110+
if (!globPatterns || globPatterns.length === 0) {
111+
globPatterns = [getDefaultPattern(typescript)];
112112
}
113-
const glob = new Glob(patterns, {
113+
const glob = new Glob(globPatterns, {
114114
__proto__: null,
115115
cwd,
116116
exclude: (name) => name === 'node_modules',
117117
});
118118
const results = glob.globSync();
119119

120120
if (hasUserSuppliedPattern && results.length === 0 && ArrayPrototypeEvery(glob.matchers, (m) => !m.hasMagic())) {
121-
console.error(`Could not find '${ArrayPrototypeJoin(patterns, ', ')}'`);
121+
console.error(`Could not find '${ArrayPrototypeJoin(globPatterns, ', ')}'`);
122122
process.exit(kGenericUserError);
123123
}
124124

@@ -130,7 +130,7 @@ function filterExecArgv(arg, i, arr) {
130130
!ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
131131
}
132132

133-
function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, testSkipPatterns, only }) {
133+
function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, testSkipPatterns, typescript, only }) {
134134
const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
135135
if (forceExit === true) {
136136
ArrayPrototypePush(argv, '--test-force-exit');
@@ -147,6 +147,9 @@ function getRunArgs(path, { forceExit, inspectPort, testNamePatterns, testSkipPa
147147
if (only === true) {
148148
ArrayPrototypePush(argv, '--test-only');
149149
}
150+
if (typescript !== 'none') {
151+
ArrayPrototypePush(argv, `--experimental-${typescript}-types`);
152+
}
150153

151154
if (path === kIsolatedProcessName) {
152155
ArrayPrototypePush(argv, '--test', ...ArrayPrototypeSlice(process.argv, 1));
@@ -473,7 +476,7 @@ function watchFiles(testFiles, opts) {
473476
// Watch for changes in current filtered files
474477
watcher.on('changed', ({ owners, eventType }) => {
475478
if (!opts.hasFiles && (eventType === 'rename' || eventType === 'change')) {
476-
const updatedTestFiles = createTestFileList(opts.globPatterns, opts.cwd);
479+
const updatedTestFiles = createTestFileList(opts);
477480
const newFileName = ArrayPrototypeFind(updatedTestFiles, (x) => !ArrayPrototypeIncludes(testFiles, x));
478481
const previousFileName = ArrayPrototypeFind(testFiles, (x) => !ArrayPrototypeIncludes(updatedTestFiles, x));
479482

@@ -540,6 +543,7 @@ function run(options = kEmptyObject) {
540543
forceExit,
541544
inspectPort,
542545
isolation = 'process',
546+
typescript = 'none',
543547
watch,
544548
setup,
545549
only,
@@ -626,6 +630,7 @@ function run(options = kEmptyObject) {
626630
});
627631
}
628632
validateOneOf(isolation, 'options.isolation', ['process', 'none']);
633+
validateOneOf(typescript, 'options.typescript', ['strip', 'transform', 'none']);
629634
validateBoolean(coverage, 'options.coverage');
630635
if (coverageExcludeGlobs != null) {
631636
if (!ArrayIsArray(coverageExcludeGlobs)) {
@@ -662,12 +667,6 @@ function run(options = kEmptyObject) {
662667
// This const should be replaced by a run option in the future.
663668
const cwd = process.cwd();
664669

665-
let testFiles = files ?? createTestFileList(globPatterns, cwd);
666-
667-
if (shard) {
668-
testFiles = ArrayPrototypeFilter(testFiles, (_, index) => index % shard.total === shard.index - 1);
669-
}
670-
671670
let teardown;
672671
let postRun;
673672
let filesWatcher;
@@ -685,8 +684,15 @@ function run(options = kEmptyObject) {
685684
forceExit,
686685
cwd,
687686
isolation,
687+
typescript,
688688
};
689689

690+
let testFiles = files ?? createTestFileList(opts);
691+
692+
if (shard) {
693+
testFiles = ArrayPrototypeFilter(testFiles, (_, index) => index % shard.total === shard.index - 1);
694+
}
695+
690696
if (isolation === 'process') {
691697
if (process.env.NODE_TEST_CONTEXT !== undefined) {
692698
process.emitWarning('node:test run() is being called recursively within a test file. skipping running files.');

lib/internal/test_runner/utils.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,15 @@ const coverageColors = {
5353
const kMultipleCallbackInvocations = 'multipleCallbackInvocations';
5454
const kRegExpPattern = /^\/(.*)\/([a-z]*)$/;
5555

56-
const kPatterns = ['test', 'test/**/*', 'test-*', '*[._-]test'];
57-
const kDefaultPattern = `**/{${ArrayPrototypeJoin(kPatterns, ',')}}.?(c|m)js`;
58-
56+
const kPatterns = ArrayPrototypeJoin(['test', 'test/**/*', 'test-*', '*[._-]test'], ',');
57+
const kJavaScriptExtensions = ArrayPrototypeJoin(['js', 'mjs', 'cjs'], ',');
58+
const kTypeScriptExtensions = ArrayPrototypeJoin(['ts', 'mts', 'cts'], ',');
59+
const kDefaultJavaScriptPattern = `**/{${kPatterns}}.{${kJavaScriptExtensions}}`;
60+
const kDefaultTypeScriptPattern = `**/{${kPatterns}}.{${kJavaScriptExtensions}${kTypeScriptExtensions}}`;
61+
62+
function getDefaultPattern(typescript) {
63+
return typescript ? kDefaultTypeScriptPattern : kDefaultJavaScriptPattern;
64+
}
5965

6066
function createDeferredCallback() {
6167
let calledCount = 0;
@@ -595,7 +601,7 @@ module.exports = {
595601
countCompletedTest,
596602
createDeferredCallback,
597603
isTestFailureError,
598-
kDefaultPattern,
604+
getDefaultPattern,
599605
parseCommandLine,
600606
reporterScope,
601607
shouldColorizeTestFiles,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const { tap } = require('node:test/reporters');
2+
const { run } = require('node:test');
3+
4+
run({ typescript: process.argv[2], files: process.argv[3] ? [process.argv[3]] : undefined })
5+
.compose(tap)
6+
.pipe(process.stdout);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { test } from 'node:test';
2+
3+
// 'as string' is stripped
4+
test('strip.ts' as string);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { test } from 'node:test';
2+
3+
test('test.ts');
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { test } from 'node:test';
2+
3+
enum ENUM {
4+
VALUE = 'transform.ts'
5+
}
6+
7+
test(ENUM.VALUE);

test/parallel/test-runner-run.mjs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,85 @@ describe('require(\'node:test\').run', { concurrency: true }, () => {
514514
for await (const _ of stream);
515515
assert.match(stderr, /Warning: node:test run\(\) is being called recursively/);
516516
});
517+
518+
describe('TypeScript support', () => {
519+
const tsCwd = fixtures.path('test-runner/run-typescript');
520+
it('throws for invalid values', () => {
521+
[Symbol(), {}, 0, 1, '1', Promise.resolve([])].forEach((typescript) => {
522+
assert.throws(() => run({ typescript }), {
523+
code: 'ERR_INVALID_ARG_VALUE',
524+
message: /The property 'options\.typescript' must be one of: 'strip', 'transform', 'none'/
525+
});
526+
});
527+
});
528+
529+
it(`'none' does nothing`, () => {
530+
it('finds TypeScript files', async () => {
531+
const spawned = await common.spawnPromisified(process.execPath,
532+
['run-tests.js', 'none'], { cwd: tsCwd });
533+
534+
assert.doesNotMatch(spawned.stdout, /ok 1 - test\.ts/);
535+
assert.strictEqual(spawned.stderr, '');
536+
assert.strictEqual(spawned.signal, null);
537+
assert.strictEqual(spawned.code, 0);
538+
});
539+
});
540+
541+
describe(`'strip'`, () => {
542+
it('finds TypeScript files', async () => {
543+
const spawned = await common.spawnPromisified(process.execPath,
544+
['run-tests.js', 'strip'], { cwd: tsCwd });
545+
546+
assert.match(spawned.stdout, /ok 1 - test\.ts/);
547+
assert.strictEqual(spawned.stderr, '');
548+
assert.strictEqual(spawned.signal, null);
549+
assert.strictEqual(spawned.code, 0);
550+
});
551+
552+
it('strips TypeScript files', async () => {
553+
const spawned = await common.spawnPromisified(process.execPath,
554+
['run-tests.js', 'strip', 'strip.ts'], { cwd: tsCwd });
555+
556+
assert.match(spawned.stdout, /ok 1 - strip\.ts/);
557+
assert.strictEqual(spawned.stderr, '');
558+
assert.strictEqual(spawned.signal, null);
559+
assert.strictEqual(spawned.code, 0);
560+
});
561+
562+
it('does not transform TypeScript files', async () => {
563+
const spawned = await common.spawnPromisified(process.execPath,
564+
['run-tests.js', 'strip', 'transform.ts'], { cwd: tsCwd });
565+
566+
assert.match(spawned.stdout, /TypeScript enum is not supported in strip-only mode/);
567+
assert.match(spawned.stdout, /not ok 1 - transform\.ts/);
568+
assert.strictEqual(spawned.stderr, '');
569+
assert.strictEqual(spawned.signal, null);
570+
assert.strictEqual(spawned.code, 0);
571+
});
572+
});
573+
574+
describe(`'transform'`, () => {
575+
it('finds TypeScript files', async () => {
576+
const spawned = await common.spawnPromisified(process.execPath,
577+
['run-tests.js', 'transform'], { cwd: tsCwd });
578+
579+
assert.match(spawned.stdout, /ok 1 - test\.ts/);
580+
assert.strictEqual(spawned.stderr, '');
581+
assert.strictEqual(spawned.signal, null);
582+
assert.strictEqual(spawned.code, 0);
583+
});
584+
585+
it('transforms TypeScript files', async () => {
586+
const spawned = await common.spawnPromisified(process.execPath,
587+
['run-tests.js', 'transform', 'transform.ts'], { cwd: tsCwd });
588+
589+
assert.match(spawned.stdout, /ok 1 - transform\.ts/);
590+
assert.strictEqual(spawned.stderr, '');
591+
assert.strictEqual(spawned.signal, null);
592+
assert.strictEqual(spawned.code, 0);
593+
});
594+
});
595+
});
517596
});
518597

519598
describe('forceExit', () => {

0 commit comments

Comments
 (0)