vitest 3.2.0-beta.2 → 3.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/browser.d.ts +3 -3
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{base.DwtwORaC.js → base.D4119yLM.js} +4 -3
  4. package/dist/chunks/{benchmark.BoF7jW0Q.js → benchmark.Cf_PACH1.js} +1 -1
  5. package/dist/chunks/{cac.I9MLYfT-.js → cac.DWaWHIIE.js} +18 -15
  6. package/dist/chunks/{cli-api.d6IK1pnk.js → cli-api.CnmEXkxs.js} +250 -49
  7. package/dist/chunks/{config.d.UqE-KR0o.d.ts → config.d.D2ROskhv.d.ts} +2 -0
  8. package/dist/chunks/{console.K1NMVOSc.js → console.Cwr-MFPV.js} +3 -2
  9. package/dist/chunks/{constants.BZZyIeIE.js → constants.DnKduX2e.js} +1 -0
  10. package/dist/chunks/{coverage.OGU09Jbh.js → coverage.C73DaDgS.js} +116 -12
  11. package/dist/chunks/{creator.DGAdZ4Hj.js → creator.C8WKy2eW.js} +10 -7
  12. package/dist/chunks/{date.CDOsz-HY.js → date.ByMsSlOr.js} +25 -0
  13. package/dist/chunks/{defaults.DSxsTG0h.js → defaults.DpVH7vbg.js} +1 -0
  14. package/dist/chunks/{environment.d.D8YDy2v5.d.ts → environment.d.cL3nLXbE.d.ts} +1 -0
  15. package/dist/chunks/{execute.JlGHLJZT.js → execute.B3q-2LPV.js} +25 -0
  16. package/dist/chunks/{global.d.BPa1eL3O.d.ts → global.d.BNLIi6yo.d.ts} +3 -1
  17. package/dist/chunks/{globals.CpxW8ccg.js → globals.CI21aWXF.js} +7 -6
  18. package/dist/chunks/{index.DFXFpH3w.js → index.2jgTs_Q5.js} +19 -1
  19. package/dist/chunks/{index.CV36oG_L.js → index.Bter3jj9.js} +83 -16
  20. package/dist/chunks/{index.DswW_LEs.js → index.CbT4iuwc.js} +7 -4
  21. package/dist/chunks/index.D3XRDfWc.js +213 -0
  22. package/dist/chunks/{index.CfXMNXHg.js → index.DNgLEKsQ.js} +4 -2
  23. package/dist/chunks/{index.CmC5OK9L.js → index.JOzufsrU.js} +2 -1
  24. package/dist/chunks/{inspector.DbDkSkFn.js → inspector.BFsh5KO0.js} +3 -0
  25. package/dist/chunks/{node.3xsWotC9.js → node.Be-ntJnD.js} +1 -1
  26. package/dist/chunks/{reporters.d.CLC9rhKy.d.ts → reporters.d.Bt4IGtsa.d.ts} +24 -6
  27. package/dist/chunks/{rpc.D9_013TY.js → rpc.BKExFSRG.js} +2 -1
  28. package/dist/chunks/{runBaseTests.Dn2vyej_.js → runBaseTests.B_M1TTsK.js} +19 -10
  29. package/dist/chunks/{setup-common.CYo3Y0dD.js → setup-common.CF-O-dZX.js} +2 -1
  30. package/dist/chunks/{typechecker.DnTrplSJ.js → typechecker.BgzF-6iO.js} +78 -21
  31. package/dist/chunks/{utils.CgTj3MsC.js → utils.BlI4TC7Y.js} +1 -0
  32. package/dist/chunks/{utils.BfxieIyZ.js → utils.DPCq3gzW.js} +3 -0
  33. package/dist/chunks/{vi.BFR5YIgu.js → vi.pkoYCV6A.js} +25 -2
  34. package/dist/chunks/{vite.d.CBZ3M_ru.d.ts → vite.d.B-Kx3KCF.d.ts} +3 -1
  35. package/dist/chunks/{vm.C1HHjtNS.js → vm.DPYem2so.js} +72 -4
  36. package/dist/chunks/{worker.d.CoCI7hzP.d.ts → worker.d.BKbBp2ga.d.ts} +2 -2
  37. package/dist/chunks/{worker.d.D5Xdi-Zr.d.ts → worker.d.Bl1O4kuf.d.ts} +1 -1
  38. package/dist/cli.js +4 -4
  39. package/dist/config.cjs +2 -0
  40. package/dist/config.d.ts +7 -6
  41. package/dist/config.js +2 -2
  42. package/dist/coverage.d.ts +4 -4
  43. package/dist/coverage.js +5 -5
  44. package/dist/environments.d.ts +6 -2
  45. package/dist/environments.js +1 -1
  46. package/dist/execute.d.ts +9 -3
  47. package/dist/execute.js +1 -1
  48. package/dist/index.d.ts +24 -12
  49. package/dist/index.js +5 -5
  50. package/dist/node.d.ts +18 -10
  51. package/dist/node.js +14 -12
  52. package/dist/reporters.d.ts +4 -4
  53. package/dist/reporters.js +3 -3
  54. package/dist/runners.d.ts +1 -1
  55. package/dist/runners.js +13 -5
  56. package/dist/snapshot.js +2 -2
  57. package/dist/suite.js +2 -2
  58. package/dist/worker.js +9 -5
  59. package/dist/workers/forks.js +6 -4
  60. package/dist/workers/runVmTests.js +14 -9
  61. package/dist/workers/threads.js +4 -4
  62. package/dist/workers/vmForks.js +6 -6
  63. package/dist/workers/vmThreads.js +6 -6
  64. package/dist/workers.d.ts +4 -4
  65. package/dist/workers.js +10 -10
  66. package/package.json +18 -18
  67. package/dist/chunks/index.CK1YOQaa.js +0 -143
@@ -5,7 +5,7 @@ import { parseStacktrace, parseErrorStacktrace } from '@vitest/utils/source-map'
5
5
  import { isAbsolute, relative, dirname, basename, resolve, normalize } from 'pathe';
6
6
  import c from 'tinyrainbow';
7
7
  import { i as isTTY } from './env.Dq0hM4Xv.js';
8
- import { h as hasFailedSnapshot, g as getOutputFile, T as TypeCheckError } from './typechecker.DnTrplSJ.js';
8
+ import { h as hasFailedSnapshot, g as getOutputFile, T as TypeCheckError } from './typechecker.BgzF-6iO.js';
9
9
  import { stripVTControlCharacters } from 'node:util';
10
10
  import { existsSync, readFileSync, promises } from 'node:fs';
11
11
  import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
@@ -283,6 +283,8 @@ class BaseReporter {
283
283
  let testsCount = 0;
284
284
  let failedCount = 0;
285
285
  let skippedCount = 0;
286
+ // delaying logs to calculate the test stats first
287
+ // which minimizes the amount of for loops
286
288
  const logs = [];
287
289
  const originalLog = this.log.bind(this);
288
290
  this.log = (msg) => logs.push(msg);
@@ -290,6 +292,7 @@ class BaseReporter {
290
292
  for (const child of children) {
291
293
  if (child.type === "suite") {
292
294
  const suiteState = child.state();
295
+ // Skipped suites are hidden when --hideSkippedTests, print otherwise
293
296
  if (!this.ctx.config.hideSkippedTests || suiteState !== "skipped") {
294
297
  this.printTestSuite(child);
295
298
  }
@@ -303,6 +306,7 @@ class BaseReporter {
303
306
  skippedCount++;
304
307
  }
305
308
  if (this.ctx.config.hideSkippedTests && suiteState === "skipped") {
309
+ // Skipped suites are hidden when --hideSkippedTests
306
310
  continue;
307
311
  }
308
312
  this.printTestCase(moduleState, child);
@@ -334,6 +338,7 @@ class BaseReporter {
334
338
  }
335
339
  if (testResult.state === "failed") {
336
340
  this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, c.dim(" > "))}`) + suffix);
341
+ // print short errors, full errors will be at the end in summary
337
342
  testResult.errors.forEach((error) => {
338
343
  const message = this.formatShortError(error);
339
344
  if (message) {
@@ -370,7 +375,9 @@ class BaseReporter {
370
375
  }
371
376
  return ` ${title} ${testModule.task.name} ${suffix}`;
372
377
  }
373
- printTestSuite(_suite) {}
378
+ printTestSuite(_suite) {
379
+ // Suite name is included in getTestName by default
380
+ }
374
381
  getTestName(test, separator) {
375
382
  return getTestName(test, separator);
376
383
  }
@@ -407,6 +414,7 @@ class BaseReporter {
407
414
  onWatcherRerun(files, trigger) {
408
415
  this.watchFilters = files;
409
416
  this.failedUnwatchedFiles = this.ctx.state.getTestModules().filter((testModule) => !files.includes(testModule.task.filepath) && testModule.state() === "failed");
417
+ // Update re-run count for each file
410
418
  files.forEach((filepath) => {
411
419
  let reruns = this._filesInWatchMode.get(filepath) ?? 0;
412
420
  this._filesInWatchMode.set(filepath, ++reruns);
@@ -449,6 +457,7 @@ class BaseReporter {
449
457
  }
450
458
  write(c.gray(log.type + c.dim(` | ${headerText}\n`)) + log.content);
451
459
  if (log.origin) {
460
+ // browser logs don't have an extra end of line at the end like Node.js does
452
461
  if (log.browser) {
453
462
  write("\n");
454
463
  }
@@ -520,6 +529,7 @@ class BaseReporter {
520
529
  this.log(padSummaryTitle("Duration"), formatTime(collectTime + testsTime + setupTime));
521
530
  } else {
522
531
  const blobs = this.ctx.state.blobs;
532
+ // Execution time is either sum of all runs of `--merge-reports` or the current run's time
523
533
  const executionTime = blobs?.executionTimes ? sum(blobs.executionTimes, (time) => time) : this.end - this.start;
524
534
  const environmentTime = sum(files, (file) => file.environmentLoad);
525
535
  const prepareTime = sum(files, (file) => file.prepareDuration);
@@ -585,6 +595,7 @@ class BaseReporter {
585
595
  printTaskErrors(tasks, errorDivider) {
586
596
  const errorsQueue = [];
587
597
  for (const task of tasks) {
598
+ // Merge identical errors
588
599
  task.result?.errors?.forEach((error) => {
589
600
  let previous;
590
601
  if (error?.stackStr) {
@@ -642,6 +653,7 @@ class BasicReporter extends BaseReporter {
642
653
  ctx.logger.deprecate(`'basic' reporter is deprecated and will be removed in Vitest v3.\n` + `Remove 'basic' from 'reporters' option. To match 'basic' reporter 100%, use configuration:\n${JSON.stringify({ test: { reporters: [["default", { summary: false }]] } }, null, 2)}`);
643
654
  }
644
655
  reportSummary(files, errors) {
656
+ // non-tty mode doesn't add a new line
645
657
  this.ctx.logger.log();
646
658
  return super.reportSummary(files, errors);
647
659
  }
@@ -809,6 +821,7 @@ async function writeBlob(content, filename) {
809
821
  await writeFile(filename, report, "utf-8");
810
822
  }
811
823
  async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
824
+ // using process.cwd() because --merge-reports can only be used in CLI
812
825
  const resolvedDir = resolve(process.cwd(), blobsDirectory);
813
826
  const blobsFiles = await readdir(resolvedDir);
814
827
  const promises = blobsFiles.map(async (filename) => {
@@ -843,6 +856,7 @@ async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
843
856
  if (!versions.has(currentVersion)) {
844
857
  throw new Error(`the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`);
845
858
  }
859
+ // fake module graph - it is used to check if module is imported, but we don't use values inside
846
860
  const projects = Object.fromEntries(projectsArray.map((p) => [p.name, p]));
847
861
  blobs.forEach((blob) => {
848
862
  blob.moduleKeys.forEach(([projectName, moduleIds]) => {
@@ -903,6 +917,7 @@ class WindowRenderer {
903
917
  error: options.logger.errorStream.write.bind(options.logger.errorStream)
904
918
  };
905
919
  this.cleanups.push(this.interceptStream(process.stdout, "output"), this.interceptStream(process.stderr, "error"));
920
+ // Write buffered content on unexpected exits, e.g. direct `process.exit()` calls
906
921
  this.options.logger.onTerminalCleanup(() => {
907
922
  this.flushBuffer();
908
923
  this.stop();
@@ -943,6 +958,7 @@ class WindowRenderer {
943
958
  return this.render();
944
959
  }
945
960
  let current;
961
+ // Concatenate same types into a single render
946
962
  for (const next of this.buffer.splice(0)) {
947
963
  if (!current) {
948
964
  current = next;
@@ -994,6 +1010,7 @@ class WindowRenderer {
994
1010
  }
995
1011
  interceptStream(stream, type) {
996
1012
  const original = stream.write;
1013
+ // @ts-expect-error -- not sure how 2 overloads should be typed
997
1014
  stream.write = (chunk, _, callback) => {
998
1015
  if (chunk) {
999
1016
  if (this.finished) {
@@ -1077,6 +1094,7 @@ class SummaryReporter {
1077
1094
  clearInterval(this.durationInterval);
1078
1095
  }
1079
1096
  onTestModuleQueued(module) {
1097
+ // When new test module starts, take the place of previously finished test module, if any
1080
1098
  if (this.finishedModules.size) {
1081
1099
  const finished = this.finishedModules.keys().next().value;
1082
1100
  this.removeTestModule(finished);
@@ -1123,6 +1141,7 @@ class SummaryReporter {
1123
1141
  stats.hook.visible = false;
1124
1142
  }
1125
1143
  onTestCaseReady(test) {
1144
+ // Track slow running tests only on verbose mode
1126
1145
  if (!this.options.verbose) {
1127
1146
  return;
1128
1147
  }
@@ -1176,16 +1195,22 @@ class SummaryReporter {
1176
1195
  this.modules.skipped++;
1177
1196
  }
1178
1197
  const left = this.modules.total - this.modules.completed;
1198
+ // Keep finished tests visible in summary for a while if there are more tests left.
1199
+ // When a new test starts in onTestModuleQueued it will take this ones place.
1200
+ // This reduces flickering by making summary more stable.
1179
1201
  if (left > this.maxParallelTests) {
1180
1202
  this.finishedModules.set(module.id, setTimeout(() => {
1181
1203
  this.removeTestModule(module.id);
1182
1204
  }, FINISHED_TEST_CLEANUP_TIME_MS).unref());
1183
1205
  } else {
1206
+ // Run is about to end as there are less tests left than whole run had parallel at max.
1207
+ // Remove finished test immediately.
1184
1208
  this.removeTestModule(module.id);
1185
1209
  }
1186
1210
  this.renderer.schedule();
1187
1211
  }
1188
1212
  getHookStats({ entity }) {
1213
+ // Track slow running hooks only on verbose mode
1189
1214
  if (!this.options.verbose) {
1190
1215
  return;
1191
1216
  }
@@ -1300,6 +1325,14 @@ class DefaultReporter extends BaseReporter {
1300
1325
  }
1301
1326
  }
1302
1327
  onTestRunStart(specifications) {
1328
+ if (this.isTTY) {
1329
+ if (this.renderSucceed === undefined) {
1330
+ this.renderSucceed = !!this.renderSucceed;
1331
+ }
1332
+ if (this.renderSucceed !== true) {
1333
+ this.renderSucceed = specifications.length <= 1;
1334
+ }
1335
+ }
1303
1336
  this.summary?.onTestRunStart(specifications);
1304
1337
  }
1305
1338
  onTestModuleQueued(file) {
@@ -1329,16 +1362,6 @@ class DefaultReporter extends BaseReporter {
1329
1362
  super.onInit(ctx);
1330
1363
  this.summary?.onInit(ctx, { verbose: this.verbose });
1331
1364
  }
1332
- onPathsCollected(paths = []) {
1333
- if (this.isTTY) {
1334
- if (this.renderSucceed === undefined) {
1335
- this.renderSucceed = !!this.renderSucceed;
1336
- }
1337
- if (this.renderSucceed !== true) {
1338
- this.renderSucceed = paths.length <= 1;
1339
- }
1340
- }
1341
- }
1342
1365
  onTestRunEnd() {
1343
1366
  this.summary?.onTestRunEnd();
1344
1367
  }
@@ -1379,6 +1402,7 @@ class DotReporter extends BaseReporter {
1379
1402
  }
1380
1403
  onTestModuleCollected(module) {
1381
1404
  for (const test of module.children.allTests()) {
1405
+ // Dot reporter marks pending tests as running
1382
1406
  this.onTestCaseReady(test);
1383
1407
  }
1384
1408
  }
@@ -1408,6 +1432,7 @@ class DotReporter extends BaseReporter {
1408
1432
  if (finishedTests.length < columns) {
1409
1433
  return;
1410
1434
  }
1435
+ // Remove finished tests from state and render them in static output
1411
1436
  const states = [];
1412
1437
  let count = 0;
1413
1438
  for (const [id, state] of finishedTests) {
@@ -1424,6 +1449,7 @@ class DotReporter extends BaseReporter {
1424
1449
  return [formatTests(Array.from(this.tests.values())), ""];
1425
1450
  }
1426
1451
  }
1452
+ // These are compared with reference equality in formatTests
1427
1453
  const pass = {
1428
1454
  char: "·",
1429
1455
  color: c.green
@@ -1463,6 +1489,7 @@ function formatTests(states) {
1463
1489
  continue;
1464
1490
  }
1465
1491
  output += currentIcon.color(currentIcon.char.repeat(count));
1492
+ // Start tracking new group
1466
1493
  count = 1;
1467
1494
  currentIcon = icon;
1468
1495
  }
@@ -1470,6 +1497,7 @@ function formatTests(states) {
1470
1497
  return output;
1471
1498
  }
1472
1499
 
1500
+ // use Logger with custom Console to capture entire error printing
1473
1501
  function capturePrintError(error, ctx, options) {
1474
1502
  let output = "";
1475
1503
  const writable = new Writable({ write(chunk, _encoding, callback) {
@@ -1499,9 +1527,12 @@ function printError(error, ctx, logger, options) {
1499
1527
  screenshotPaths: options.screenshotPaths,
1500
1528
  printProperties: options.verbose,
1501
1529
  parseErrorStacktrace(error) {
1530
+ // browser stack trace needs to be processed differently,
1531
+ // so there is a separate method for that
1502
1532
  if (options.task?.file.pool === "browser" && project.browser) {
1503
1533
  return project.browser.parseErrorStacktrace(error, { ignoreStackEntries: options.fullStack ? [] : undefined });
1504
1534
  }
1535
+ // node.js stack trace already has correct source map locations
1505
1536
  return parseErrorStacktrace(error, {
1506
1537
  frameFilter: project.config.onStackTrace,
1507
1538
  ignoreStackEntries: options.fullStack ? [] : undefined
@@ -1526,6 +1557,7 @@ function printErrorInner(error, project, options) {
1526
1557
  stack: error.stack
1527
1558
  };
1528
1559
  }
1560
+ // Error may have occurred even before the configuration was resolved
1529
1561
  if (!project) {
1530
1562
  printErrorMessage(e, logger);
1531
1563
  return;
@@ -1554,6 +1586,7 @@ function printErrorInner(error, project, options) {
1554
1586
  logger.error(`${e.codeFrame}\n`);
1555
1587
  }
1556
1588
  if ("__vitest_rollup_error__" in e) {
1589
+ // https://github.com/vitejs/vite/blob/95020ab49e12d143262859e095025cf02423c1d9/packages/vite/src/node/server/middlewares/error.ts#L25-L36
1557
1590
  const err = e.__vitest_rollup_error__;
1558
1591
  logger.error([
1559
1592
  err.plugin && ` Plugin: ${c.magenta(err.plugin)}`,
@@ -1561,9 +1594,11 @@ function printErrorInner(error, project, options) {
1561
1594
  err.frame && c.yellow(err.frame.split(/\r?\n/g).map((l) => ` `.repeat(2) + l).join(`\n`))
1562
1595
  ].filter(Boolean).join("\n"));
1563
1596
  }
1597
+ // E.g. AssertionError from assert does not set showDiff but has both actual and expected properties
1564
1598
  if (e.diff) {
1565
1599
  logger.error(`\n${e.diff}\n`);
1566
1600
  }
1601
+ // if the error provide the frame
1567
1602
  if (e.frame) {
1568
1603
  logger.error(c.yellow(e.frame));
1569
1604
  } else {
@@ -1578,6 +1613,7 @@ function printErrorInner(error, project, options) {
1578
1613
  const testPath = e.VITEST_TEST_PATH;
1579
1614
  const testName = e.VITEST_TEST_NAME;
1580
1615
  const afterEnvTeardown = e.VITEST_AFTER_ENV_TEARDOWN;
1616
+ // testName has testPath inside
1581
1617
  if (testPath) {
1582
1618
  logger.error(c.red(`This error originated in "${c.bold(relative(project.config.root, testPath))}" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.`));
1583
1619
  }
@@ -1603,7 +1639,6 @@ function printErrorType(type, ctx) {
1603
1639
  }
1604
1640
  const skipErrorProperties = new Set([
1605
1641
  "nameStr",
1606
- "stack",
1607
1642
  "cause",
1608
1643
  "stacks",
1609
1644
  "stackStr",
@@ -1634,7 +1669,10 @@ function getErrorProperties(e) {
1634
1669
  return errorObject;
1635
1670
  }
1636
1671
  for (const key of Object.getOwnPropertyNames(e)) {
1637
- if (!skipErrorProperties.has(key)) {
1672
+ // print the original stack if it was ever changed manually by the user
1673
+ if (key === "stack" && e[key] != null && typeof e[key] !== "string") {
1674
+ errorObject[key] = e[key];
1675
+ } else if (key !== "stack" && !skipErrorProperties.has(key)) {
1638
1676
  errorObject[key] = e[key];
1639
1677
  }
1640
1678
  }
@@ -1681,6 +1719,7 @@ function printErrorMessage(error, logger) {
1681
1719
  return;
1682
1720
  }
1683
1721
  if (error.message.length > 5e3) {
1722
+ // Protect against infinite stack trace in tinyrainbow
1684
1723
  logger.error(`${c.red(c.bold(errorName))}: ${error.message}`);
1685
1724
  } else {
1686
1725
  logger.error(c.red(`${c.bold(errorName)}: ${error.message}`));
@@ -1703,6 +1742,7 @@ function printStack(logger, project, stack, highlight, errorProperties, onStack)
1703
1742
  }
1704
1743
  }
1705
1744
  function hasProperties(obj) {
1745
+ // eslint-disable-next-line no-unreachable-loop
1706
1746
  for (const _key in obj) {
1707
1747
  return true;
1708
1748
  }
@@ -1724,11 +1764,13 @@ function generateCodeFrame(source, indent = 0, loc, range = 2) {
1724
1764
  continue;
1725
1765
  }
1726
1766
  const lineLength = lines[j].length;
1767
+ // too long, maybe it's a minified file, skip for codeframe
1727
1768
  if (stripVTControlCharacters(lines[j]).length > 200) {
1728
1769
  return "";
1729
1770
  }
1730
1771
  res.push(lineNo(j + 1) + truncateString(lines[j].replace(/\t/g, " "), columns - 5 - indent));
1731
1772
  if (j === i) {
1773
+ // push underline
1732
1774
  const pad = start - (count - lineLength) + (nl - 1);
1733
1775
  const length = Math.max(1, end > count ? lineLength - pad : end - start);
1734
1776
  res.push(lineNo() + " ".repeat(pad) + c.red("^".repeat(length)));
@@ -1754,10 +1796,15 @@ function lineNo(no = "") {
1754
1796
 
1755
1797
  class GithubActionsReporter {
1756
1798
  ctx = undefined;
1799
+ options;
1800
+ constructor(options = {}) {
1801
+ this.options = options;
1802
+ }
1757
1803
  onInit(ctx) {
1758
1804
  this.ctx = ctx;
1759
1805
  }
1760
1806
  onFinished(files = [], errors = []) {
1807
+ // collect all errors and associate them with projects
1761
1808
  const projectErrors = new Array();
1762
1809
  for (const error of errors) {
1763
1810
  projectErrors.push({
@@ -1784,6 +1831,8 @@ class GithubActionsReporter {
1784
1831
  }
1785
1832
  }
1786
1833
  }
1834
+ const onWritePath = this.options.onWritePath ?? defaultOnWritePath;
1835
+ // format errors via `printError`
1787
1836
  for (const { project, title, error, file } of projectErrors) {
1788
1837
  const result = capturePrintError(error, this.ctx, {
1789
1838
  project,
@@ -1796,7 +1845,7 @@ class GithubActionsReporter {
1796
1845
  const formatted = formatMessage({
1797
1846
  command: "error",
1798
1847
  properties: {
1799
- file: stack.file,
1848
+ file: onWritePath(stack.file),
1800
1849
  title,
1801
1850
  line: String(stack.line),
1802
1851
  column: String(stack.column)
@@ -1807,6 +1856,12 @@ class GithubActionsReporter {
1807
1856
  }
1808
1857
  }
1809
1858
  }
1859
+ function defaultOnWritePath(path) {
1860
+ return path;
1861
+ }
1862
+ // workflow command formatting based on
1863
+ // https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message
1864
+ // https://github.com/actions/toolkit/blob/f1d9b4b985e6f0f728b4b766db73498403fd5ca3/packages/core/src/command.ts#L80-L85
1810
1865
  function formatMessage({ command, properties, message }) {
1811
1866
  let result = `::${command}`;
1812
1867
  Object.entries(properties).forEach(([k, v], i) => {
@@ -1976,16 +2031,18 @@ function flattenTasks$1(task, baseName = "") {
1976
2031
  }];
1977
2032
  }
1978
2033
  }
2034
+ // https://gist.github.com/john-doherty/b9195065884cdbfd2017a4756e6409cc
1979
2035
  function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
1980
2036
  let regex = /([\0-\x08\v\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g;
1981
2037
  value = String(value || "").replace(regex, "");
1982
2038
  {
2039
+ // remove everything discouraged by XML 1.0 specifications
1983
2040
  regex = new RegExp(
1984
2041
  /* eslint-disable regexp/prefer-character-class, regexp/no-obscure-range, regexp/no-useless-non-capturing-group */
1985
2042
  "([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\uDF" + "FE\\uDFFF])|\\uD8BF[\\uDFFE\\uDFFF]|\\uD8FF[\\uDFFE\\uDFFF]|(?:\\uD93F[\\uDFFE\\uD" + "FFF])|\\uD97F[\\uDFFE\\uDFFF]|\\uD9BF[\\uDFFE\\uDFFF]|\\uD9FF[\\uDFFE\\uDFFF]" + "|\\uDA3F[\\uDFFE\\uDFFF]|\\uDA7F[\\uDFFE\\uDFFF]|\\uDABF[\\uDFFE\\uDFFF]|(?:\\" + "uDAFF[\\uDFFE\\uDFFF])|\\uDB3F[\\uDFFE\\uDFFF]|\\uDB7F[\\uDFFE\\uDFFF]|(?:\\uDBBF" + "[\\uDFFE\\uDFFF])|\\uDBFF[\\uDFFE\\uDFFF](?:[\\0-\\t\\v\\f\\x0E-\\u2027\\u202A-\\uD7FF\\" + "uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|" + "(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))",
1986
2043
  "g"
1987
2044
  /* eslint-enable */
1988
- );
2045
+ );
1989
2046
  value = value.replace(regex, "");
1990
2047
  }
1991
2048
  return value;
@@ -2131,6 +2188,7 @@ class JUnitReporter {
2131
2188
  failures: 0,
2132
2189
  skipped: 0
2133
2190
  });
2191
+ // inject failed suites to surface errors during beforeAll/afterAll
2134
2192
  const suites = getSuites(file);
2135
2193
  for (const suite of suites) {
2136
2194
  if (suite.result?.errors) {
@@ -2138,6 +2196,7 @@ class JUnitReporter {
2138
2196
  stats.failures += 1;
2139
2197
  }
2140
2198
  }
2199
+ // If there are no tests, but the file failed to load, we still want to report it as a failure
2141
2200
  if (tasks.length === 0 && file.result?.state === "fail") {
2142
2201
  stats.failures = 1;
2143
2202
  tasks.push({
@@ -2228,6 +2287,7 @@ class TapReporter {
2228
2287
  this.logger.log(`name: ${yamlString(String(errorName))}`);
2229
2288
  this.logger.log(`message: ${yamlString(String(error.message))}`);
2230
2289
  if (stack) {
2290
+ // For compatibility with tap-mocha-reporter
2231
2291
  this.logger.log(`stack: ${yamlString(`${stack.file}:${stack.line}:${stack.column}`)}`);
2232
2292
  }
2233
2293
  }
@@ -2302,12 +2362,18 @@ class VerboseReporter extends DefaultReporter {
2302
2362
  verbose = true;
2303
2363
  renderSucceed = true;
2304
2364
  printTestModule(module) {
2365
+ // still print the test module in TTY,
2366
+ // but don't print it in the CLI because we
2367
+ // print all the tests when they finish
2368
+ // instead of printing them when the test file finishes
2305
2369
  if (this.isTTY) {
2306
2370
  return super.printTestModule(module);
2307
2371
  }
2308
2372
  }
2309
2373
  onTestCaseResult(test) {
2310
2374
  super.onTestCaseResult(test);
2375
+ // don't print tests in TTY as they go, only print them
2376
+ // in the CLI when they finish
2311
2377
  if (this.isTTY) {
2312
2378
  return;
2313
2379
  }
@@ -2346,6 +2412,7 @@ class VerboseReporter extends DefaultReporter {
2346
2412
  return " ".repeat(getIndentation(test));
2347
2413
  }
2348
2414
  formatShortError() {
2415
+ // Short errors are not shown in tree-view
2349
2416
  return "";
2350
2417
  }
2351
2418
  }
@@ -1,9 +1,9 @@
1
1
  import * as chai from 'chai';
2
2
  import { resolve } from 'node:path';
3
- import { l as loadDiffConfig, b as loadSnapshotSerializers, t as takeCoverageInsideWorker } from './setup-common.CYo3Y0dD.js';
3
+ import { l as loadDiffConfig, b as loadSnapshotSerializers, t as takeCoverageInsideWorker } from './setup-common.CF-O-dZX.js';
4
4
  import { distDir } from '../path.js';
5
- import { r as rpc } from './rpc.D9_013TY.js';
6
- import { g as getWorkerState } from './utils.CgTj3MsC.js';
5
+ import { r as rpc } from './rpc.BKExFSRG.js';
6
+ import { g as getWorkerState } from './utils.BlI4TC7Y.js';
7
7
 
8
8
  function setupChaiConfig(config) {
9
9
  Object.assign(chai.config, config);
@@ -11,7 +11,7 @@ function setupChaiConfig(config) {
11
11
 
12
12
  async function resolveSnapshotEnvironment(config, executor) {
13
13
  if (!config.snapshotEnvironment) {
14
- const { VitestNodeSnapshotEnvironment } = await import('./node.3xsWotC9.js');
14
+ const { VitestNodeSnapshotEnvironment } = await import('./node.Be-ntJnD.js');
15
15
  return new VitestNodeSnapshotEnvironment();
16
16
  }
17
17
  const mod = await executor.executeId(config.snapshotEnvironment);
@@ -36,6 +36,7 @@ async function getTestRunnerConstructor(config, executor) {
36
36
  async function resolveTestRunner(config, executor) {
37
37
  const TestRunner = await getTestRunnerConstructor(config, executor);
38
38
  const testRunner = new TestRunner(config);
39
+ // inject private executor to every runner
39
40
  Object.defineProperty(testRunner, "__vitest_executor", {
40
41
  value: executor,
41
42
  enumerable: false,
@@ -49,6 +50,7 @@ async function resolveTestRunner(config, executor) {
49
50
  }
50
51
  const [diffOptions] = await Promise.all([loadDiffConfig(config, executor), loadSnapshotSerializers(config, executor)]);
51
52
  testRunner.config.diffOptions = diffOptions;
53
+ // patch some methods, so custom runners don't need to call RPC
52
54
  const originalOnTaskUpdate = testRunner.onTaskUpdate;
53
55
  testRunner.onTaskUpdate = async (task, events) => {
54
56
  const p = rpc().onTaskUpdate(task, events);
@@ -66,6 +68,7 @@ async function resolveTestRunner(config, executor) {
66
68
  files.forEach((file) => {
67
69
  file.prepareDuration = state.durations.prepare;
68
70
  file.environmentLoad = state.durations.environment;
71
+ // should be collected only for a single test file in a batch
69
72
  state.durations.prepare = 0;
70
73
  state.durations.environment = 0;
71
74
  });