vitest 4.0.0-beta.10 → 4.0.0-beta.12

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 (69) hide show
  1. package/LICENSE.md +85 -101
  2. package/dist/browser.d.ts +10 -6
  3. package/dist/browser.js +8 -5
  4. package/dist/chunks/{benchmark.CJUa-Hsa.js → benchmark.DHKMYAts.js} +2 -2
  5. package/dist/chunks/{browser.d.yFAklsD1.d.ts → browser.d.D9YV3JvA.d.ts} +1 -1
  6. package/dist/chunks/{cac.DCxo_nSu.js → cac.r1gel_VZ.js} +18 -11
  7. package/dist/chunks/{cli-api.BJJXh9BV.js → cli-api.CpywZzJV.js} +153 -176
  8. package/dist/chunks/{config.d.B_LthbQq.d.ts → config.d.DGazh2r6.d.ts} +3 -1
  9. package/dist/chunks/{console.7h5kHUIf.js → console.CTJL2nuH.js} +4 -6
  10. package/dist/chunks/{coverage.BCU-r2QL.js → coverage.CiB0fs_7.js} +57 -79
  11. package/dist/chunks/{creator.08Gi-vCA.js → creator.DfXDsUyL.js} +6 -8
  12. package/dist/chunks/{global.d.BK3X7FW1.d.ts → global.d.BcFPD2LN.d.ts} +0 -13
  13. package/dist/chunks/{globals.DG-S3xFe.js → globals.DC4ntO86.js} +8 -6
  14. package/dist/chunks/{index.DIWhzsUh.js → index.Bt-upxGS.js} +6 -12
  15. package/dist/chunks/{index.BIP7prJq.js → index.CHrBLuEH.js} +94 -103
  16. package/dist/chunks/{index.X0nbfr6-.js → index.Dc3xnDvT.js} +48 -289
  17. package/dist/chunks/{index.CMfqw92x.js → index.Dnl38iQ_.js} +2 -2
  18. package/dist/chunks/{index.BjKEiSn0.js → index.uLUz1RDt.js} +3 -3
  19. package/dist/chunks/{inspector.CvQD-Nie.js → inspector.Br76Q2Mb.js} +1 -4
  20. package/dist/chunks/{moduleRunner.d.D9nBoC4p.d.ts → moduleRunner.d.CeYc7nZ0.d.ts} +1 -1
  21. package/dist/chunks/{node.CyipiPvJ.js → node.BwAWWjHZ.js} +3 -4
  22. package/dist/chunks/{plugin.d.BMVSnsGV.d.ts → plugin.d.XreRXLXS.d.ts} +1 -1
  23. package/dist/chunks/{reporters.d.BUWjmRYq.d.ts → reporters.d.CJVTaaWb.d.ts} +54 -13
  24. package/dist/chunks/{resolveSnapshotEnvironment.Bkht6Yor.js → resolveSnapshotEnvironment.BsJpmVZR.js} +7 -8
  25. package/dist/chunks/{rpc.BKr6mtxz.js → rpc.cD77ENhU.js} +13 -14
  26. package/dist/chunks/{setup-common.uiMcU3cv.js → setup-common.BewgbkTd.js} +6 -6
  27. package/dist/chunks/{startModuleRunner.p67gbNo9.js → startModuleRunner.DPBo3mme.js} +65 -56
  28. package/dist/chunks/{test.BiqSKISg.js → test.CTuWuHYH.js} +7 -7
  29. package/dist/chunks/{typechecker.DB-fIMaH.js → typechecker.BfOQ86_a.js} +624 -14
  30. package/dist/chunks/{utils.D2R2NiOH.js → utils.CG9h5ccR.js} +2 -5
  31. package/dist/chunks/{vi.ZPgvtBao.js → vi.B2--mG9U.js} +38 -145
  32. package/dist/{worker.js → chunks/worker.DVTUM2IW.js} +71 -42
  33. package/dist/chunks/{worker.d.BDsXGkwh.d.ts → worker.d.buwuBpBt.d.ts} +2 -77
  34. package/dist/cli.js +7 -5
  35. package/dist/config.d.ts +6 -6
  36. package/dist/coverage.d.ts +5 -5
  37. package/dist/coverage.js +4 -5
  38. package/dist/environments.js +1 -1
  39. package/dist/index.d.ts +8 -8
  40. package/dist/index.js +8 -6
  41. package/dist/module-evaluator.d.ts +3 -3
  42. package/dist/module-evaluator.js +11 -13
  43. package/dist/module-runner.js +5 -5
  44. package/dist/node.d.ts +14 -11
  45. package/dist/node.js +18 -14
  46. package/dist/reporters.d.ts +5 -5
  47. package/dist/reporters.js +7 -5
  48. package/dist/runners.d.ts +1 -1
  49. package/dist/runners.js +9 -7
  50. package/dist/snapshot.js +3 -3
  51. package/dist/suite.js +4 -3
  52. package/dist/{chunks/base.Cjha6usc.js → worker-base.js} +104 -32
  53. package/dist/{chunks/vm.Ca0Y0W5f.js → worker-vm.js} +81 -31
  54. package/dist/workers/runVmTests.js +14 -11
  55. package/package.json +26 -26
  56. package/browser.d.ts +0 -1
  57. package/dist/chunks/moduleTransport.I-bgQy0S.js +0 -19
  58. package/dist/chunks/resolver.Bx6lE0iq.js +0 -119
  59. package/dist/chunks/utils.C2YI6McM.js +0 -52
  60. package/dist/chunks/worker.d.BNcX_2mH.d.ts +0 -8
  61. package/dist/workers/forks.js +0 -67
  62. package/dist/workers/threads.js +0 -55
  63. package/dist/workers/vmForks.js +0 -48
  64. package/dist/workers/vmThreads.js +0 -38
  65. package/dist/workers.d.ts +0 -38
  66. package/dist/workers.js +0 -48
  67. package/execute.d.ts +0 -1
  68. package/utils.d.ts +0 -1
  69. package/workers.d.ts +0 -1
@@ -1,16 +1,18 @@
1
1
  import { existsSync, readFileSync, promises } from 'node:fs';
2
2
  import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
3
3
  import { resolve, dirname, isAbsolute, relative, basename, normalize } from 'pathe';
4
- import { g as getOutputFile, h as hasFailedSnapshot, T as TypeCheckError } from './typechecker.DB-fIMaH.js';
4
+ import { g as getOutputFile, h as hasFailedSnapshot, T as TypeCheckError } from './typechecker.BfOQ86_a.js';
5
5
  import { performance as performance$1 } from 'node:perf_hooks';
6
- import { getTestName, getFullName, hasFailed, getTests, getSuites, getTasks } from '@vitest/runner/utils';
7
- import { slash, toArray, isPrimitive, inspect, positionToOffset, lineSplitRE } from '@vitest/utils';
6
+ import { getTestName, hasFailed, getTests, getSuites, getTasks, getFullName } from '@vitest/runner/utils';
7
+ import { slash, toArray, isPrimitive } from '@vitest/utils/helpers';
8
8
  import { parseStacktrace, parseErrorStacktrace, defaultStackIgnorePatterns } from '@vitest/utils/source-map';
9
9
  import c from 'tinyrainbow';
10
10
  import { i as isTTY } from './env.D4Lgay0q.js';
11
11
  import { stripVTControlCharacters } from 'node:util';
12
12
  import { Console } from 'node:console';
13
13
  import { Writable } from 'node:stream';
14
+ import { inspect } from '@vitest/utils/display';
15
+ import { positionToOffset, lineSplitRE } from '@vitest/utils/offset';
14
16
  import { createRequire } from 'node:module';
15
17
  import { hostname } from 'node:os';
16
18
 
@@ -169,9 +171,9 @@ async function writeBlob(content, filename) {
169
171
  }
170
172
  async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
171
173
  // using process.cwd() because --merge-reports can only be used in CLI
172
- const resolvedDir = resolve(process.cwd(), blobsDirectory), blobsFiles = await readdir(resolvedDir), promises = blobsFiles.map(async (filename) => {
173
- const fullPath = resolve(resolvedDir, filename), stats = await stat(fullPath);
174
- if (!stats.isFile()) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`);
174
+ const resolvedDir = resolve(process.cwd(), blobsDirectory), promises = (await readdir(resolvedDir)).map(async (filename) => {
175
+ const fullPath = resolve(resolvedDir, filename);
176
+ if (!(await stat(fullPath)).isFile()) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`);
175
177
  const content = await readFile(fullPath, "utf-8"), [version, files, errors, moduleKeys, coverage, executionTime] = parse(content);
176
178
  if (!version) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`);
177
179
  return {
@@ -232,6 +234,7 @@ const testPass = c.green(F_CHECK);
232
234
  const taskFail = c.red(F_CROSS);
233
235
  const suiteFail = c.red(F_POINTER);
234
236
  const pending$1 = c.gray("·");
237
+ const separator = c.dim(" > ");
235
238
  const labelDefaultColors = [
236
239
  c.bgYellow,
237
240
  c.bgCyan,
@@ -351,6 +354,7 @@ var utils = /*#__PURE__*/Object.freeze({
351
354
  pending: pending$1,
352
355
  pointer: pointer,
353
356
  renderSnapshotSummary: renderSnapshotSummary,
357
+ separator: separator,
354
358
  skipped: skipped,
355
359
  suiteFail: suiteFail,
356
360
  taskFail: taskFail,
@@ -440,19 +444,10 @@ class BaseReporter {
440
444
  })), logs.forEach((log) => this.log(log));
441
445
  }
442
446
  printTestCase(moduleState, test) {
443
- const testResult = test.result(), { duration, retryCount, repeatCount } = test.diagnostic() || {}, padding = this.getTestIndentation(test.task);
444
- let suffix = this.getDurationPrefix(test.task);
445
- if (retryCount != null && retryCount > 0) suffix += c.yellow(` (retry x${retryCount})`);
446
- if (repeatCount != null && repeatCount > 0) suffix += c.yellow(` (repeat x${repeatCount})`);
447
- if (testResult.state === "failed")
448
- // print short errors, full errors will be at the end in summary
449
- this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, c.dim(" > "))}`) + suffix), testResult.errors.forEach((error) => {
450
- const message = this.formatShortError(error);
451
- if (message) this.log(c.red(` ${padding}${message}`));
452
- });
453
- else if (duration && duration > this.ctx.config.slowTestThreshold) this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, c.dim(" > "))} ${suffix}`);
454
- else if (this.ctx.config.hideSkippedTests && testResult.state === "skipped") ; else if (testResult.state === "skipped" && testResult.note) this.log(` ${padding}${getStateSymbol(test.task)} ${this.getTestName(test.task, c.dim(" > "))}${c.dim(c.gray(` [${testResult.note}]`))}`);
455
- else if (this.renderSucceed || moduleState === "failed") this.log(` ${padding}${getStateSymbol(test.task)} ${this.getTestName(test.task, c.dim(" > "))}${suffix}`);
447
+ const testResult = test.result(), { duration = 0 } = test.diagnostic() || {}, padding = this.getTestIndentation(test.task), suffix = this.getTestCaseSuffix(test);
448
+ if (testResult.state === "failed") this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, separator)}`) + suffix);
449
+ else if (duration > this.ctx.config.slowTestThreshold) this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, separator)} ${suffix}`);
450
+ else if (this.ctx.config.hideSkippedTests && testResult.state === "skipped") ; else if (this.renderSucceed || moduleState === "failed") this.log(` ${padding}${this.getStateSymbol(test)} ${this.getTestName(test.task, separator)}${suffix}`);
456
451
  }
457
452
  getModuleLog(testModule, counts) {
458
453
  let state = c.dim(`${counts.tests} test${counts.tests > 1 ? "s" : ""}`);
@@ -461,46 +456,65 @@ class BaseReporter {
461
456
  let suffix = c.dim("(") + state + c.dim(")") + this.getDurationPrefix(testModule.task);
462
457
  const diagnostic = testModule.diagnostic();
463
458
  if (diagnostic.heap != null) suffix += c.magenta(` ${Math.floor(diagnostic.heap / 1024 / 1024)} MB heap used`);
464
- let title = getStateSymbol(testModule.task);
465
- if (testModule.meta().typecheck) title += ` ${c.bgBlue(c.bold(" TS "))}`;
466
- if (testModule.project.name) title += ` ${formatProjectName(testModule.project, "")}`;
467
- return ` ${title} ${testModule.task.name} ${suffix}`;
459
+ return ` ${this.getEntityPrefix(testModule)} ${testModule.task.name} ${suffix}`;
468
460
  }
469
- printTestSuite(_suite) {
470
- // Suite name is included in getTestName by default
461
+ printTestSuite(testSuite) {
462
+ if (!this.renderSucceed) return;
463
+ const indentation = " ".repeat(getIndentation(testSuite.task)), tests = Array.from(testSuite.children.allTests()), state = this.getStateSymbol(testSuite);
464
+ this.log(` ${indentation}${state} ${testSuite.name} ${c.dim(`(${tests.length})`)}`);
471
465
  }
472
- getTestName(test, separator) {
473
- return getTestName(test, separator);
466
+ getTestName(test, _separator) {
467
+ return test.name;
474
468
  }
475
469
  getFullName(test, separator) {
476
- return getFullName(test, separator);
477
- }
478
- formatShortError(error) {
479
- return `${F_RIGHT} ${error.message}`;
470
+ if (test === test.file) return test.name;
471
+ let name = test.file.name;
472
+ if (test.location) name += c.dim(`:${test.location.line}:${test.location.column}`);
473
+ return name += separator, name += getTestName(test, separator), name;
480
474
  }
481
- getTestIndentation(_test) {
482
- return " ";
475
+ getTestIndentation(test) {
476
+ return " ".repeat(getIndentation(test));
483
477
  }
484
478
  printAnnotations(test, console, padding = 0) {
485
479
  const annotations = test.annotations();
486
480
  if (!annotations.length) return;
487
- const PADDING = " ".repeat(padding);
488
- annotations.forEach(({ location, type, message }) => {
481
+ const PADDING = " ".repeat(padding), groupedAnnotations = {};
482
+ for (const group in annotations.forEach((annotation) => {
483
+ const { location, type } = annotation;
484
+ let group;
489
485
  if (location) {
490
486
  const file = relative(test.project.config.root, location.file);
491
- this[console](`${PADDING}${c.blue(F_POINTER)} ${c.gray(`${file}:${location.line}:${location.column}`)} ${c.bold(type)}`);
492
- } else this[console](`${PADDING}${c.blue(F_POINTER)} ${c.bold(type)}`);
487
+ group = `${c.gray(`${file}:${location.line}:${location.column}`)} ${c.bold(type)}`;
488
+ } else group = c.bold(type);
489
+ groupedAnnotations[group] ??= [], groupedAnnotations[group].push(annotation);
490
+ }), groupedAnnotations) this[console](`${PADDING}${c.blue(F_POINTER)} ${group}`), groupedAnnotations[group].forEach(({ message }) => {
493
491
  this[console](`${PADDING} ${c.blue(F_DOWN_RIGHT)} ${message}`);
494
492
  });
495
493
  }
494
+ getEntityPrefix(entity) {
495
+ let title = this.getStateSymbol(entity);
496
+ if (entity.project.name) title += ` ${formatProjectName(entity.project, "")}`;
497
+ if (entity.meta().typecheck) title += ` ${c.bgBlue(c.bold(" TS "))}`;
498
+ return title;
499
+ }
500
+ getTestCaseSuffix(testCase) {
501
+ const { heap, retryCount, repeatCount } = testCase.diagnostic() || {}, testResult = testCase.result();
502
+ let suffix = this.getDurationPrefix(testCase.task);
503
+ if (retryCount != null && retryCount > 0) suffix += c.yellow(` (retry x${retryCount})`);
504
+ if (repeatCount != null && repeatCount > 0) suffix += c.yellow(` (repeat x${repeatCount})`);
505
+ if (heap != null) suffix += c.magenta(` ${Math.floor(heap / 1024 / 1024)} MB heap used`);
506
+ if (testResult.state === "skipped" && testResult.note) suffix += c.dim(c.gray(` [${testResult.note}]`));
507
+ return suffix;
508
+ }
509
+ getStateSymbol(test) {
510
+ return getStateSymbol(test.task);
511
+ }
496
512
  getDurationPrefix(task) {
497
- if (!task.result?.duration) return "";
498
- const color = task.result.duration > this.ctx.config.slowTestThreshold ? c.yellow : c.green;
499
- return color(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
513
+ const duration = task.result?.duration && Math.round(task.result?.duration);
514
+ return duration == null ? "" : (duration > this.ctx.config.slowTestThreshold ? c.yellow : c.green)(` ${duration}${c.dim("ms")}`);
500
515
  }
501
516
  onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
502
- const failed = errors.length > 0 || hasFailed(files);
503
- if (failed) this.log(withLabel("red", "FAIL", "Tests failed. Watching for file changes..."));
517
+ if (errors.length > 0 || hasFailed(files)) this.log(withLabel("red", "FAIL", "Tests failed. Watching for file changes..."));
504
518
  else if (this.ctx.isCancelling) this.log(withLabel("red", "CANCELLED", "Test run cancelled. Watching for file changes..."));
505
519
  else this.log(withLabel("green", "PASS", "Waiting for file changes..."));
506
520
  const hints = [c.dim("press ") + c.bold("h") + c.dim(" to show help")];
@@ -531,7 +545,7 @@ class BaseReporter {
531
545
  const output = log.type === "stdout" ? this.ctx.logger.outputStream : this.ctx.logger.errorStream, write = (msg) => output.write(msg);
532
546
  let headerText = "unknown test";
533
547
  const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0;
534
- if (task) headerText = this.getFullName(task, c.dim(" > "));
548
+ if (task) headerText = this.getFullName(task, separator);
535
549
  else if (log.taskId && log.taskId !== "__vitest__unknown_test__") headerText = log.taskId;
536
550
  if (write(c.gray(log.type + c.dim(` | ${headerText}\n`)) + log.content), log.origin) {
537
551
  // browser logs don't have an extra end of line at the end like Node.js does
@@ -550,8 +564,8 @@ class BaseReporter {
550
564
  shouldLog(log, taskState) {
551
565
  if (this.ctx.config.silent === true || this.ctx.config.silent === "passed-only" && taskState !== "failed") return false;
552
566
  if (this.ctx.config.onConsoleLog) {
553
- const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0, entity = task && this.ctx.state.getReportedEntity(task), shouldLog = this.ctx.config.onConsoleLog(log.content, log.type, entity);
554
- if (shouldLog === false) return shouldLog;
567
+ const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0, entity = task && this.ctx.state.getReportedEntity(task);
568
+ if (this.ctx.config.onConsoleLog(log.content, log.type, entity) === false) return false;
555
569
  }
556
570
  return true;
557
571
  }
@@ -601,12 +615,12 @@ class BaseReporter {
601
615
  if (errors.length) this.ctx.logger.printUnhandledErrors(errors), this.error();
602
616
  }
603
617
  reportBenchmarkSummary(files) {
604
- const benches = getTests(files), topBenches = benches.filter((i) => i.result?.benchmark?.rank === 1);
618
+ const topBenches = getTests(files).filter((i) => i.result?.benchmark?.rank === 1);
605
619
  this.log(`\n${withLabel("cyan", "BENCH", "Summary\n")}`);
606
620
  for (const bench of topBenches) {
607
621
  const group = bench.suite || bench.file;
608
622
  if (!group) continue;
609
- const groupName = this.getFullName(group, c.dim(" > ")), project = this.ctx.projects.find((p) => p.name === bench.file.projectName);
623
+ const groupName = this.getFullName(group, separator), project = this.ctx.projects.find((p) => p.name === bench.file.projectName);
610
624
  this.log(` ${formatProjectName(project)}${bench.name}${c.dim(` - ${groupName}`)}`);
611
625
  const siblings = group.tasks.filter((i) => i.meta.benchmark && i.result?.benchmark && i !== bench).sort((a, b) => a.result.benchmark.rank - b.result.benchmark.rank);
612
626
  for (const sibling of siblings) {
@@ -633,7 +647,7 @@ class BaseReporter {
633
647
  for (const [error, tasks] of errorsQueue) {
634
648
  for (const task of tasks) {
635
649
  const filepath = task?.filepath || "", projectName = task?.projectName || task.file?.projectName || "", project = this.ctx.projects.find((p) => p.name === projectName);
636
- let name = this.getFullName(task, c.dim(" > "));
650
+ let name = this.getFullName(task, separator);
637
651
  if (filepath) name += c.dim(` [ ${this.relative(filepath)} ]`);
638
652
  this.ctx.logger.error(`${c.bgRed(c.bold(" FAIL "))} ${formatProjectName(project)}${name}`);
639
653
  }
@@ -664,6 +678,9 @@ function sum(items, cb) {
664
678
  return total + Math.max(cb(next) || 0, 0);
665
679
  }, 0);
666
680
  }
681
+ function getIndentation(suite, level = 1) {
682
+ return suite.suite && !("filepath" in suite.suite) ? getIndentation(suite.suite, level + 1) : level;
683
+ }
667
684
 
668
685
  const DEFAULT_RENDER_INTERVAL_MS = 1e3, ESC = "\x1B[", CLEAR_LINE = `${ESC}K`, MOVE_CURSOR_ONE_ROW_UP = `${ESC}1A`, SYNC_START = `${ESC}?2026h`, SYNC_END = `${ESC}?2026l`;
669
686
  /**
@@ -876,11 +893,10 @@ class SummaryReporter {
876
893
  else if (state === "failed") this.modules.failed++;
877
894
  else if (module.task.mode === "todo" && state === "skipped") this.modules.todo++;
878
895
  else if (state === "skipped") this.modules.skipped++;
879
- const left = this.modules.total - this.modules.completed;
880
896
  // Keep finished tests visible in summary for a while if there are more tests left.
881
897
  // When a new test starts in onTestModuleQueued it will take this ones place.
882
898
  // This reduces flickering by making summary more stable.
883
- if (left > this.maxParallelTests) this.finishedModules.set(module.id, setTimeout(() => {
899
+ if (this.modules.total - this.modules.completed > this.maxParallelTests) this.finishedModules.set(module.id, setTimeout(() => {
884
900
  this.removeTestModule(module.id);
885
901
  }, FINISHED_TEST_CLEANUP_TIME_MS).unref());
886
902
  else
@@ -1105,12 +1121,12 @@ function capturePrintError(error, ctx, options) {
1105
1121
  } }), console = new Console(writable), logger = {
1106
1122
  error: console.error.bind(console),
1107
1123
  highlight: ctx.logger.highlight.bind(ctx.logger)
1108
- }, result = printError(error, ctx, logger, {
1109
- showCodeFrame: false,
1110
- ...options
1111
- });
1124
+ };
1112
1125
  return {
1113
- nearest: result?.nearest,
1126
+ nearest: printError(error, ctx, logger, {
1127
+ showCodeFrame: false,
1128
+ ...options
1129
+ })?.nearest,
1114
1130
  output
1115
1131
  };
1116
1132
  }
@@ -1158,19 +1174,17 @@ function printErrorInner(error, project, options) {
1158
1174
  const stacks = options.parseErrorStacktrace(e), nearest = error instanceof TypeCheckError ? error.stacks[0] : stacks.find((stack) => {
1159
1175
  // we are checking that this module was processed by us at one point
1160
1176
  try {
1161
- const environments = [...Object.values(project._vite?.environments || {}), ...Object.values(project.browser?.vite.environments || {})], hasResult = environments.some((environment) => {
1162
- const modules = environment.moduleGraph.getModulesByFile(stack.file);
1163
- return [...modules?.values() || []].some((module) => !!module.transformResult);
1164
- });
1165
- return hasResult && existsSync(stack.file);
1177
+ return [...Object.values(project._vite?.environments || {}), ...Object.values(project.browser?.vite.environments || {})].some((environment) => {
1178
+ return [...environment.moduleGraph.getModulesByFile(stack.file)?.values() || []].some((module) => !!module.transformResult);
1179
+ }) && existsSync(stack.file);
1166
1180
  } catch {
1167
1181
  return false;
1168
1182
  }
1169
1183
  });
1170
1184
  if (type) printErrorType(type, project.vitest);
1171
1185
  if (printErrorMessage(e, logger), options.screenshotPaths?.length) {
1172
- const length = options.screenshotPaths.length;
1173
- if (logger.error(`\nFailure screenshot${length > 1 ? "s" : ""}:`), logger.error(options.screenshotPaths.map((p) => ` - ${c.dim(relative(process.cwd(), p))}`).join("\n")), !e.diff) logger.error();
1186
+ const uniqueScreenshots = Array.from(new Set(options.screenshotPaths)), length = uniqueScreenshots.length;
1187
+ if (logger.error(`\nFailure screenshot${length > 1 ? "s" : ""}:`), logger.error(uniqueScreenshots.map((p) => ` - ${c.dim(relative(process.cwd(), p))}`).join("\n")), !e.diff) logger.error();
1174
1188
  }
1175
1189
  if (e.codeFrame) logger.error(`${e.codeFrame}\n`);
1176
1190
  if ("__vitest_rollup_error__" in e) {
@@ -1252,7 +1266,7 @@ function handleImportOutsideModuleError(stack, logger) {
1252
1266
  if (!esmErrors.some((e) => stack.includes(e))) return;
1253
1267
  const path = normalize(stack.split("\n")[0].trim());
1254
1268
  let name = path.split("/node_modules/").pop() || "";
1255
- if (name?.startsWith("@")) name = name.split("/").slice(0, 2).join("/");
1269
+ if (name[0] === "@") name = name.split("/").slice(0, 2).join("/");
1256
1270
  else name = name.split("/")[0];
1257
1271
  if (name) printModuleWarningForPackage(logger, path, name);
1258
1272
  else printModuleWarningForSourceCode(logger, path);
@@ -1409,7 +1423,7 @@ const BUILT_IN_TYPES = [
1409
1423
  "warning"
1410
1424
  ];
1411
1425
  function getTitle(type) {
1412
- return BUILT_IN_TYPES.includes(type) ? void 0 : type;
1426
+ if (!BUILT_IN_TYPES.includes(type)) return type;
1413
1427
  }
1414
1428
  function getType(type) {
1415
1429
  return BUILT_IN_TYPES.includes(type) ? type : "notice";
@@ -1436,8 +1450,7 @@ function escapeProperty(s) {
1436
1450
  class HangingProcessReporter {
1437
1451
  whyRunning;
1438
1452
  onInit() {
1439
- const _require = createRequire(import.meta.url);
1440
- this.whyRunning = _require("why-is-node-running");
1453
+ this.whyRunning = createRequire(import.meta.url)("why-is-node-running");
1441
1454
  }
1442
1455
  onProcessTimeout() {
1443
1456
  this.whyRunning?.();
@@ -1597,8 +1610,7 @@ class JUnitReporter {
1597
1610
  this.reportFile = resolve(this.ctx.config.root, outputFile);
1598
1611
  const outputDirectory = dirname(this.reportFile);
1599
1612
  if (!existsSync(outputDirectory)) await promises.mkdir(outputDirectory, { recursive: true });
1600
- const fileFd = await promises.open(this.reportFile, "w+");
1601
- this.fileFd = fileFd, this.baseLog = async (text) => {
1613
+ this.fileFd = await promises.open(this.reportFile, "w+"), this.baseLog = async (text) => {
1602
1614
  if (!this.fileFd) this.fileFd = await promises.open(this.reportFile, "w+");
1603
1615
  await promises.writeFile(this.fileFd, `${text}\n`);
1604
1616
  };
@@ -1761,7 +1773,7 @@ class TapReporter {
1761
1773
  this.logger.log(`# ${type}: ${message}`);
1762
1774
  }), this.logger.unindent();
1763
1775
  if (task.result?.state === "fail" && task.result.errors) this.logger.indent(), task.result.errors.forEach((error) => {
1764
- const stacks = task.file.pool === "browser" ? project.browser?.parseErrorStacktrace(error) || [] : parseErrorStacktrace(error, { frameFilter: this.ctx.config.onStackTrace }), stack = stacks[0];
1776
+ const stack = (task.file.pool === "browser" ? project.browser?.parseErrorStacktrace(error) || [] : parseErrorStacktrace(error, { frameFilter: this.ctx.config.onStackTrace }))[0];
1765
1777
  if (this.logger.log("---"), this.logger.log("error:"), this.logger.indent(), this.logErrorDetails(error), this.logger.unindent(), stack) this.logger.log(`at: ${yamlString(`${stack.file}:${stack.line}:${stack.column}`)}`);
1766
1778
  if (error.showDiff) this.logger.log(`actual: ${yamlString(error.actual)}`), this.logger.log(`expected: ${yamlString(error.expected)}`);
1767
1779
  }), this.logger.log("..."), this.logger.unindent();
@@ -1792,48 +1804,26 @@ class TapFlatReporter extends TapReporter {
1792
1804
  }
1793
1805
  }
1794
1806
 
1807
+ class TreeReporter extends DefaultReporter {
1808
+ verbose = true;
1809
+ renderSucceed = true;
1810
+ }
1811
+
1795
1812
  class VerboseReporter extends DefaultReporter {
1796
1813
  verbose = true;
1797
1814
  renderSucceed = true;
1798
- printTestModule(module) {
1799
- // still print the test module in TTY,
1800
- // but don't print it in the CLI because we
1801
- // print all the tests when they finish
1802
- // instead of printing them when the test file finishes
1803
- if (this.isTTY) return super.printTestModule(module);
1815
+ printTestModule(_module) {
1816
+ // don't print test module, only print tests
1804
1817
  }
1805
1818
  onTestCaseResult(test) {
1806
- // don't print tests in TTY as they go, only print them
1807
- // in the CLI when they finish
1808
- if (super.onTestCaseResult(test), this.isTTY) return;
1819
+ super.onTestCaseResult(test);
1809
1820
  const testResult = test.result();
1810
1821
  if (this.ctx.config.hideSkippedTests && testResult.state === "skipped") return;
1811
- let title = ` ${getStateSymbol(test.task)} `;
1812
- if (test.project.name) title += formatProjectName(test.project);
1813
- title += getFullName(test.task, c.dim(" > ")), title += this.getDurationPrefix(test.task);
1814
- const diagnostic = test.diagnostic();
1815
- if (diagnostic?.heap != null) title += c.magenta(` ${Math.floor(diagnostic.heap / 1024 / 1024)} MB heap used`);
1816
- if (testResult.state === "skipped" && testResult.note) title += c.dim(c.gray(` [${testResult.note}]`));
1817
- if (this.log(title), testResult.state === "failed") testResult.errors.forEach((error) => this.log(c.red(` ${F_RIGHT} ${error?.message}`)));
1822
+ let title = ` ${this.getEntityPrefix(test)} `;
1823
+ if (title += test.module.task.name, test.location) title += c.dim(`:${test.location.line}:${test.location.column}`);
1824
+ if (title += separator, title += getTestName(test.task, separator), title += this.getTestCaseSuffix(test), this.log(title), testResult.state === "failed") testResult.errors.forEach((error) => this.log(c.red(` ${F_RIGHT} ${error.message}`)));
1818
1825
  if (test.annotations().length) this.log(), this.printAnnotations(test, "log", 3), this.log();
1819
1826
  }
1820
- printTestSuite(testSuite) {
1821
- const indentation = " ".repeat(getIndentation(testSuite.task)), tests = Array.from(testSuite.children.allTests()), state = getStateSymbol(testSuite.task);
1822
- this.log(` ${indentation}${state} ${testSuite.name} ${c.dim(`(${tests.length})`)}`);
1823
- }
1824
- getTestName(test) {
1825
- return test.name;
1826
- }
1827
- getTestIndentation(test) {
1828
- return " ".repeat(getIndentation(test));
1829
- }
1830
- formatShortError() {
1831
- // Short errors are not shown in tree-view
1832
- return "";
1833
- }
1834
- }
1835
- function getIndentation(suite, level = 1) {
1836
- return suite.suite && !("filepath" in suite.suite) ? getIndentation(suite.suite, level + 1) : level;
1837
1827
  }
1838
1828
 
1839
1829
  const ReportersMap = {
@@ -1845,8 +1835,9 @@ const ReportersMap = {
1845
1835
  "tap": TapReporter,
1846
1836
  "tap-flat": TapFlatReporter,
1847
1837
  "junit": JUnitReporter,
1838
+ "tree": TreeReporter,
1848
1839
  "hanging-process": HangingProcessReporter,
1849
1840
  "github-actions": GithubActionsReporter
1850
1841
  };
1851
1842
 
1852
- export { BlobReporter as B, DefaultReporter as D, F_RIGHT as F, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter as J, ReportersMap as R, TapFlatReporter as T, VerboseReporter as V, DotReporter as a, JUnitReporter as b, TapReporter as c, printError as d, errorBanner as e, formatProjectName as f, getStateSymbol as g, divider as h, generateCodeFrame as i, parse as p, readBlobs as r, stringify as s, truncateString as t, utils as u, withLabel as w };
1843
+ export { BlobReporter as B, DefaultReporter as D, F_RIGHT as F, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter as J, ReportersMap as R, TapFlatReporter as T, VerboseReporter as V, DotReporter as a, JUnitReporter as b, TapReporter as c, stringify as d, printError as e, formatProjectName as f, getStateSymbol as g, errorBanner as h, divider as i, generateCodeFrame as j, parse as p, readBlobs as r, separator as s, truncateString as t, utils as u, withLabel as w };