| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 27a696f commit fa18b17
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -709,6 +709,10 @@ unless a destination is explicitly provided. | |||
| 709 | 709 | ||
| 710 | 710 | <!-- YAML | |
| 711 | 711 | added: v18.9.0 | |
| 712 | + changes: | ||
| 713 | + - version: REPLACEME | ||
| 714 | + pr-url: https://github.com/nodejs/node/pull/47628 | ||
| 715 | + description: Add a testNamePatterns option. | ||
| 712 | 716 | --> | |
| 713 | 717 | ||
| 714 | 718 | * `options` {Object} Configuration options for running tests. The following | |
@@ -734,6 +738,12 @@ added: v18.9.0 | |||
| 734 | 738 | number. If a nullish value is provided, each process gets its own port, | |
| 735 | 739 | incremented from the primary's `process.debugPort`. | |
| 736 | 740 | **Default:** `undefined`. | |
| 741 | + * `testNamePatterns` {string|RegExp|Array} A String, RegExp or a RegExp Array, | ||
| 742 | + that can be used to only run tests whose name matches the provided pattern. | ||
| 743 | + Test name patterns are interpreted as JavaScript regular expressions. | ||
| 744 | + For each test that is executed, any corresponding test hooks, such as | ||
| 745 | + `beforeEach()`, are also run. | ||
| 746 | + **Default:** `undefined`. | ||
| 737 | 747 | * Returns: {TestsStream} | |
| 738 | 748 | ||
| 739 | 749 | ```mjs | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,12 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | const { | |
| 3 | 3 | ArrayFrom, | |
| 4 | + ArrayIsArray, | ||
| 4 | 5 | ArrayPrototypeFilter, | |
| 5 | 6 | ArrayPrototypeForEach, | |
| 6 | 7 | ArrayPrototypeIncludes, | |
| 7 | 8 | ArrayPrototypeIndexOf, | |
| 9 | + ArrayPrototypeMap, | ||
| 8 | 10 | ArrayPrototypePush, | |
| 9 | 11 | ArrayPrototypeSlice, | |
| 10 | 12 | ArrayPrototypeSome, | |
@@ -33,11 +35,13 @@ const { FilesWatcher } = require('internal/watch_mode/files_watcher'); | |||
| 33 | 35 | const console = require('internal/console/global'); | |
| 34 | 36 | const { | |
| 35 | 37 | codes: { | |
| 38 | + ERR_INVALID_ARG_TYPE, | ||
| 36 | 39 | ERR_TEST_FAILURE, | |
| 37 | 40 | }, | |
| 38 | 41 | } = require('internal/errors'); | |
| 39 | 42 | const { validateArray, validateBoolean, validateFunction } = require('internal/validators'); | |
| 40 | 43 | const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector'); | |
| 44 | + const { isRegExp } = require('internal/util/types'); | ||
| 41 | 45 | const { kEmptyObject } = require('internal/util'); | |
| 42 | 46 | const { createTestTree } = require('internal/test_runner/harness'); | |
| 43 | 47 | const { | |
@@ -53,6 +57,7 @@ const { YAMLToJs } = require('internal/test_runner/yaml_to_js'); | |||
| 53 | 57 | const { TokenKind } = require('internal/test_runner/tap_lexer'); | |
| 54 | 58 | ||
| 55 | 59 | const { | |
| 60 | + convertStringToRegExp, | ||
| 56 | 61 | countCompletedTest, | |
| 57 | 62 | doesPathMatchFilter, | |
| 58 | 63 | isSupportedFileType, | |
@@ -137,11 +142,14 @@ function filterExecArgv(arg, i, arr) { | |||
| 137 | 142 | !ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`)); | |
| 138 | 143 | } | |
| 139 | 144 | ||
| 140 | - function getRunArgs({ path, inspectPort }) { | ||
| 145 | + function getRunArgs({ path, inspectPort, testNamePatterns }) { | ||
| 141 | 146 | const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv); | |
| 142 | 147 | if (isUsingInspector()) { | |
| 143 | 148 | ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`); | |
| 144 | 149 | } | |
| 150 | + if (testNamePatterns) { | ||
| 151 | + ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`)); | ||
| 152 | + } | ||
| 145 | 153 | ArrayPrototypePush(argv, path); | |
| 146 | 154 | ||
| 147 | 155 | return argv; | |
@@ -255,9 +263,9 @@ class FileTest extends Test { | |||
| 255 | 263 | const runningProcesses = new SafeMap(); | |
| 256 | 264 | const runningSubtests = new SafeMap(); | |
| 257 | 265 | ||
| 258 | - function runTestFile(path, root, inspectPort, filesWatcher) { | ||
| 266 | + function runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns) { | ||
| 259 | 267 | const subtest = root.createSubtest(FileTest, path, async (t) => { | |
| 260 | - const args = getRunArgs({ path, inspectPort }); | ||
| 268 | + const args = getRunArgs({ path, inspectPort, testNamePatterns }); | ||
| 261 | 269 | const stdio = ['pipe', 'pipe', 'pipe']; | |
| 262 | 270 | const env = { ...process.env, NODE_TEST_CONTEXT: 'child' }; | |
| 263 | 271 | if (filesWatcher) { | |
@@ -339,7 +347,7 @@ function runTestFile(path, root, inspectPort, filesWatcher) { | |||
| 339 | 347 | return promise; | |
| 340 | 348 | } | |
| 341 | 349 | ||
| 342 | - function watchFiles(testFiles, root, inspectPort) { | ||
| 350 | + function watchFiles(testFiles, root, inspectPort, testNamePatterns) { | ||
| 343 | 351 | const filesWatcher = new FilesWatcher({ throttle: 500, mode: 'filter' }); | |
| 344 | 352 | filesWatcher.on('changed', ({ owners }) => { | |
| 345 | 353 | filesWatcher.unfilterFilesOwnedBy(owners); | |
@@ -353,7 +361,7 @@ function watchFiles(testFiles, root, inspectPort) { | |||
| 353 | 361 | await once(runningProcess, 'exit'); | |
| 354 | 362 | } | |
| 355 | 363 | await runningSubtests.get(file); | |
| 356 | - runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher)); | ||
| 364 | + runningSubtests.set(file, runTestFile(file, root, inspectPort, filesWatcher, testNamePatterns)); | ||
| 357 | 365 | }, undefined, (error) => { | |
| 358 | 366 | triggerUncaughtException(error, true /* fromPromise */); | |
| 359 | 367 | })); | |
@@ -365,6 +373,7 @@ function run(options) { | |||
| 365 | 373 | if (options === null || typeof options !== 'object') { | |
| 366 | 374 | options = kEmptyObject; | |
| 367 | 375 | } | |
| 376 | + let { testNamePatterns } = options; | ||
| 368 | 377 | const { concurrency, timeout, signal, files, inspectPort, watch, setup } = options; | |
| 369 | 378 | ||
| 370 | 379 | if (files != null) { | |
@@ -376,20 +385,36 @@ function run(options) { | |||
| 376 | 385 | if (setup != null) { | |
| 377 | 386 | validateFunction(setup, 'options.setup'); | |
| 378 | 387 | } | |
| 388 | + if (testNamePatterns != null) { | ||
| 389 | + if (!ArrayIsArray(testNamePatterns)) { | ||
| 390 | + testNamePatterns = [testNamePatterns]; | ||
| 391 | + } | ||
| 392 | + validateArray(testNamePatterns, 'options.testNamePatterns'); | ||
| 393 | + testNamePatterns = ArrayPrototypeMap(testNamePatterns, (value, i) => { | ||
| 394 | + if (isRegExp(value)) { | ||
| 395 | + return value; | ||
| 396 | + } | ||
| 397 | + const name = `options.testNamePatterns[${i}]`; | ||
| 398 | + if (typeof value === 'string') { | ||
| 399 | + return convertStringToRegExp(value, name); | ||
| 400 | + } | ||
| 401 | + throw new ERR_INVALID_ARG_TYPE(name, ['string', 'RegExp'], value); | ||
| 402 | + }); | ||
| 403 | + } | ||
| 379 | 404 | ||
| 380 | 405 | const root = createTestTree({ concurrency, timeout, signal }); | |
| 381 | 406 | const testFiles = files ?? createTestFileList(); | |
| 382 | 407 | ||
| 383 | 408 | let postRun = () => root.postRun(); | |
| 384 | 409 | let filesWatcher; | |
| 385 | 410 | if (watch) { | |
| 386 | - filesWatcher = watchFiles(testFiles, root, inspectPort); | ||
| 411 | + filesWatcher = watchFiles(testFiles, root, inspectPort, testNamePatterns); | ||
| 387 | 412 | postRun = undefined; | |
| 388 | 413 | } | |
| 389 | 414 | const runFiles = () => { | |
| 390 | 415 | root.harness.bootstrapComplete = true; | |
| 391 | 416 | return SafePromiseAllSettledReturnVoid(testFiles, (path) => { | |
| 392 | - const subtest = runTestFile(path, root, inspectPort, filesWatcher); | ||
| 417 | + const subtest = runTestFile(path, root, inspectPort, filesWatcher, testNamePatterns); | ||
| 393 | 418 | runningSubtests.set(path, subtest); | |
| 394 | 419 | return subtest; | |
| 395 | 420 | }); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const test = require('node:test'); | ||
| 3 | + | ||
| 4 | + test('this should be skipped'); | ||
| 5 | + test('this should be executed'); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -101,4 +101,20 @@ describe('require(\'node:test\').run', { concurrency: true }, () => { | |||
| 101 | 101 | assert.strictEqual(result[11], '# todo 0\n'); | |
| 102 | 102 | assert.match(result[12], /# duration_ms \d+\.?\d*/); | |
| 103 | 103 | }); | |
| 104 | + | ||
| 105 | + it('should skip tests not matching testNamePatterns - RegExp', async () => { | ||
| 106 | + const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: [/executed/] }) | ||
| 107 | + .compose(tap) | ||
| 108 | + .toArray(); | ||
| 109 | + assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n'); | ||
| 110 | + assert.strictEqual(result[5], 'ok 2 - this should be executed\n'); | ||
| 111 | + }); | ||
| 112 | + | ||
| 113 | + it('should skip tests not matching testNamePatterns - string', async () => { | ||
| 114 | + const result = await run({ files: [join(testFixtures, 'test/skip_by_name.cjs')], testNamePatterns: ['executed'] }) | ||
| 115 | + .compose(tap) | ||
| 116 | + .toArray(); | ||
| 117 | + assert.strictEqual(result[2], 'ok 1 - this should be skipped # SKIP test name does not match pattern\n'); | ||
| 118 | + assert.strictEqual(result[5], 'ok 2 - this should be executed\n'); | ||
| 119 | + }); | ||
| 104 | 120 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments