@@ -395,8 +395,10 @@ class Test extends AsyncResource { |
395 | 395 | this.parent = parent; |
396 | 396 | this.testNumber = 0; |
397 | 397 | this.outputSubtestCount = 0; |
398 | | - this.filteredSubtestCount = 0; |
| 398 | + this.diagnostics = []; |
399 | 399 | this.filtered = false; |
| 400 | + this.filteredByName = false; |
| 401 | + this.hasOnlyTests = false; |
400 | 402 |
|
401 | 403 | if (parent === null) { |
402 | 404 | this.root = this; |
@@ -413,26 +415,48 @@ class Test extends AsyncResource { |
413 | 415 | } else { |
414 | 416 | const nesting = parent.parent === null ? parent.nesting : |
415 | 417 | parent.nesting + 1; |
| 418 | + const { config, isFilteringByName, isFilteringByOnly } = parent.root.harness; |
416 | 419 |
|
417 | 420 | this.root = parent.root; |
418 | 421 | this.harness = null; |
419 | | - this.config = this.root.harness.config; |
| 422 | + this.config = config; |
420 | 423 | this.concurrency = parent.concurrency; |
421 | 424 | this.nesting = nesting; |
422 | | - this.only = only ?? (parent.only && !parent.runOnlySubtests); |
| 425 | + this.only = only; |
423 | 426 | this.reporter = parent.reporter; |
424 | 427 | this.runOnlySubtests = false; |
425 | 428 | this.childNumber = parent.subtests.length + 1; |
426 | 429 | this.timeout = parent.timeout; |
427 | 430 | this.entryFile = parent.entryFile; |
428 | 431 |
|
429 | | - if (this.willBeFiltered()) { |
430 | | - this.filtered = true; |
431 | | - this.parent.filteredSubtestCount++; |
| 432 | + if (isFilteringByName) { |
| 433 | + this.filteredByName = this.willBeFilteredByName(); |
| 434 | + if (!this.filteredByName) { |
| 435 | + for (let t = this.parent; t !== null && t.filteredByName; t = t.parent) { |
| 436 | + t.filteredByName = false; |
| 437 | + } |
| 438 | + } |
432 | 439 | } |
433 | 440 |
|
434 | | - if (this.config.only && only === false) { |
435 | | - fn = noop; |
| 441 | + if (isFilteringByOnly) { |
| 442 | + if (this.only) { |
| 443 | + // If filtering impacts the tests within a suite, then the suite only |
| 444 | + // runs those tests. If filtering does not impact the tests within a |
| 445 | + // suite, then all tests are run. |
| 446 | + this.parent.runOnlySubtests = true; |
| 447 | + |
| 448 | + if (this.parent === this.root || this.parent.startTime === null) { |
| 449 | + for (let t = this.parent; t !== null && !t.hasOnlyTests; t = t.parent) { |
| 450 | + t.hasOnlyTests = true; |
| 451 | + } |
| 452 | + } |
| 453 | + } else if (this.only === false) { |
| 454 | + fn = noop; |
| 455 | + } |
| 456 | + } else if (only || this.parent.runOnlySubtests) { |
| 457 | + const warning = |
| 458 | + "'only' and 'runOnly' require the --test-only command-line option."; |
| 459 | + this.diagnostic(warning); |
436 | 460 | } |
437 | 461 | } |
438 | 462 |
|
@@ -491,7 +515,6 @@ class Test extends AsyncResource { |
491 | 515 | this.endTime = null; |
492 | 516 | this.passed = false; |
493 | 517 | this.error = null; |
494 | | - this.diagnostics = []; |
495 | 518 | this.message = typeof skip === 'string' ? skip : |
496 | 519 | typeof todo === 'string' ? todo : null; |
497 | 520 | this.activeSubtests = 0; |
@@ -509,12 +532,6 @@ class Test extends AsyncResource { |
509 | 532 | ownAfterEachCount: 0, |
510 | 533 | }; |
511 | 534 |
|
512 | | - if (!this.config.only && (only || this.parent?.runOnlySubtests)) { |
513 | | - const warning = |
514 | | - "'only' and 'runOnly' require the --test-only command-line option."; |
515 | | - this.diagnostic(warning); |
516 | | - } |
517 | | - |
518 | 535 | if (loc === undefined) { |
519 | 536 | this.loc = undefined; |
520 | 537 | } else { |
@@ -542,9 +559,27 @@ class Test extends AsyncResource { |
542 | 559 | } |
543 | 560 | } |
544 | 561 |
|
545 | | - willBeFiltered() { |
546 | | - if (this.config.only && !this.only) return true; |
| 562 | + applyFilters() { |
| 563 | + if (this.error) { |
| 564 | + // Never filter out errors. |
| 565 | + return; |
| 566 | + } |
547 | 567 |
|
| 568 | + if (this.filteredByName) { |
| 569 | + this.filtered = true; |
| 570 | + return; |
| 571 | + } |
| 572 | + |
| 573 | + if (this.root.harness.isFilteringByOnly && !this.only && !this.hasOnlyTests) { |
| 574 | + if (this.parent.runOnlySubtests || |
| 575 | + this.parent.hasOnlyTests || |
| 576 | + this.only === false) { |
| 577 | + this.filtered = true; |
| 578 | + } |
| 579 | + } |
| 580 | + } |
| 581 | + |
| 582 | + willBeFilteredByName() { |
548 | 583 | const { testNamePatterns, testSkipPatterns } = this.config; |
549 | 584 |
|
550 | 585 | if (testNamePatterns && !testMatchesPattern(this, testNamePatterns)) { |
@@ -757,12 +792,10 @@ class Test extends AsyncResource { |
757 | 792 | ArrayPrototypePush(this.diagnostics, message); |
758 | 793 | } |
759 | 794 |
|
760 | | - get shouldFilter() { |
761 | | - return this.filtered && this.parent?.filteredSubtestCount > 0; |
762 | | - } |
763 | | - |
764 | 795 | start() { |
765 | | - if (this.shouldFilter) { |
| 796 | + this.applyFilters(); |
| 797 | + |
| 798 | + if (this.filtered) { |
766 | 799 | noopTestStream ??= new TestsStream(); |
767 | 800 | this.reporter = noopTestStream; |
768 | 801 | this.run = this.filteredRun; |
@@ -970,7 +1003,7 @@ class Test extends AsyncResource { |
970 | 1003 | this.mock?.reset(); |
971 | 1004 |
|
972 | 1005 | if (this.parent !== null) { |
973 | | - if (!this.shouldFilter) { |
| 1006 | + if (!this.filtered) { |
974 | 1007 | const report = this.getReportDetails(); |
975 | 1008 | report.details.passed = this.passed; |
976 | 1009 | this.testNumber ||= ++this.parent.outputSubtestCount; |
@@ -1159,7 +1192,7 @@ class TestHook extends Test { |
1159 | 1192 | getRunArgs() { |
1160 | 1193 | return this.#args; |
1161 | 1194 | } |
1162 | | - willBeFiltered() { |
| 1195 | + willBeFilteredByName() { |
1163 | 1196 | return false; |
1164 | 1197 | } |
1165 | 1198 | postRun() { |
@@ -1192,7 +1225,6 @@ class Suite extends Test { |
1192 | 1225 | this.fn = options.fn || this.fn; |
1193 | 1226 | this.skipped = false; |
1194 | 1227 | } |
1195 | | - this.runOnlySubtests = this.config.only; |
1196 | 1228 |
|
1197 | 1229 | try { |
1198 | 1230 | const { ctx, args } = this.getRunArgs(); |
@@ -1216,21 +1248,6 @@ class Suite extends Test { |
1216 | 1248 |
|
1217 | 1249 | postBuild() { |
1218 | 1250 | this.buildPhaseFinished = true; |
1219 | | - if (this.filtered && |
1220 | | - (this.filteredSubtestCount !== this.subtests.length || this.error)) { |
1221 | | - // A suite can transition from filtered to unfiltered based on the |
1222 | | - // tests that it contains - in case of children matching patterns. |
1223 | | - this.filtered = false; |
1224 | | - this.parent.filteredSubtestCount--; |
1225 | | - } else if ( |
1226 | | - this.config.only && |
1227 | | - this.config.testNamePatterns == null && |
1228 | | - this.config.testSkipPatterns == null && |
1229 | | - this.filteredSubtestCount === this.subtests.length |
1230 | | - ) { |
1231 | | - // If no subtests are marked as "only", run them all |
1232 | | - this.filteredSubtestCount = 0; |
1233 | | - } |
1234 | 1251 | } |
1235 | 1252 |
|
1236 | 1253 | getRunArgs() { |
|