vitest 4.0.0-beta.8 → 4.0.0

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 (85) hide show
  1. package/LICENSE.md +86 -102
  2. package/browser/context.d.ts +7 -0
  3. package/browser/context.js +20 -0
  4. package/dist/browser.d.ts +26 -9
  5. package/dist/browser.js +17 -7
  6. package/dist/chunks/base.CtHM3ryk.js +128 -0
  7. package/dist/chunks/{benchmark.UW6Ezvxy.js → benchmark.DHKMYAts.js} +2 -2
  8. package/dist/chunks/{browser.d.DOMmqJQx.d.ts → browser.d.B9iJzZyn.d.ts} +3 -3
  9. package/dist/chunks/{cac.By1HvRIk.js → cac.B99MQg-w.js} +47 -91
  10. package/dist/chunks/{cli-api.C-JHgQgp.js → cli-api.PwHwIMss.js} +1544 -310
  11. package/dist/chunks/{config.d._GBBbReY.d.ts → config.d.u2CUDWwS.d.ts} +6 -19
  12. package/dist/chunks/{console.B0quX7yH.js → console.CTJL2nuH.js} +4 -6
  13. package/dist/chunks/{coverage.DarITf6U.js → coverage.FU3w4IrQ.js} +128 -1142
  14. package/dist/chunks/{creator.KEg6n5IC.js → creator.DucAaYBz.js} +10 -37
  15. package/dist/chunks/{defaults.CXFFjsi8.js → defaults.BOqNVLsY.js} +0 -1
  16. package/dist/chunks/environment.d.CrsxCzP1.d.ts +29 -0
  17. package/dist/chunks/evaluatedModules.Dg1zASAC.js +17 -0
  18. package/dist/chunks/{global.d.K6uBQHzY.d.ts → global.d.BgJSTpgQ.d.ts} +2 -17
  19. package/dist/chunks/{globals.lgsmH00r.js → globals.BGT_RUsD.js} +12 -9
  20. package/dist/chunks/{index.BuwjkI-q.js → index.BdSLhLDZ.js} +3 -3
  21. package/dist/chunks/{index.DfviD7lX.js → index.CbWINfS7.js} +49 -21
  22. package/dist/chunks/{index.AzwzFtyi.js → index.CcRZ6fUh.js} +1493 -114
  23. package/dist/chunks/{index.X0nbfr6-.js → index.Dc3xnDvT.js} +48 -289
  24. package/dist/chunks/{index.AR8aAkCC.js → index.RwjEGCQ0.js} +7 -8
  25. package/dist/chunks/init-forks.DSafeltJ.js +54 -0
  26. package/dist/chunks/init-threads.SUtZ-067.js +17 -0
  27. package/dist/chunks/init.B2EESLQM.js +213 -0
  28. package/dist/chunks/{inspector.CvQD-Nie.js → inspector.DLZxSeU3.js} +2 -6
  29. package/dist/chunks/{moduleRunner.d.CX4DuqOx.d.ts → moduleRunner.d.YtNsMIoJ.d.ts} +12 -14
  30. package/dist/chunks/{node.BOqcT2jW.js → node.BwAWWjHZ.js} +3 -4
  31. package/dist/chunks/{plugin.d.CHe6slQs.d.ts → plugin.d.DQU1R5px.d.ts} +1 -1
  32. package/dist/chunks/{reporters.d.37tJQ2uV.d.ts → reporters.d.BMKt7f6I.d.ts} +1066 -1030
  33. package/dist/chunks/{index.CsFXYRkW.js → resolveSnapshotEnvironment.DJJKMKxb.js} +18 -24
  34. package/dist/chunks/{rpc.RpPylpp0.js → rpc.cD77ENhU.js} +13 -14
  35. package/dist/chunks/{setup-common.hLGRxhC8.js → setup-common.DR1sucx6.js} +8 -8
  36. package/dist/chunks/{startModuleRunner.C8TW8zTN.js → startModuleRunner.C2tTvmF9.js} +131 -110
  37. package/dist/chunks/test.C3RPt8JR.js +214 -0
  38. package/dist/chunks/{utils.C7__0Iv5.js → utils.CG9h5ccR.js} +3 -15
  39. package/dist/chunks/{vi.BfdOiD4j.js → vi.BZvkKVkM.js} +73 -176
  40. package/dist/chunks/{vm.BHBje7cC.js → vm.DBeOXrP9.js} +29 -33
  41. package/dist/chunks/{worker.d.DYlqbejz.d.ts → worker.d.BFk-vvBU.d.ts} +42 -6
  42. package/dist/cli.js +12 -11
  43. package/dist/config.cjs +0 -1
  44. package/dist/config.d.ts +12 -14
  45. package/dist/config.js +1 -1
  46. package/dist/coverage.d.ts +8 -7
  47. package/dist/coverage.js +3 -14
  48. package/dist/environments.d.ts +3 -6
  49. package/dist/environments.js +1 -1
  50. package/dist/index.d.ts +24 -30
  51. package/dist/index.js +12 -11
  52. package/dist/module-evaluator.d.ts +6 -4
  53. package/dist/module-evaluator.js +14 -16
  54. package/dist/module-runner.js +5 -5
  55. package/dist/node.d.ts +83 -27
  56. package/dist/node.js +23 -20
  57. package/dist/reporters.d.ts +11 -10
  58. package/dist/reporters.js +12 -11
  59. package/dist/runners.d.ts +1 -1
  60. package/dist/runners.js +14 -216
  61. package/dist/snapshot.js +3 -3
  62. package/dist/suite.js +4 -3
  63. package/dist/worker.d.ts +26 -0
  64. package/dist/worker.js +45 -166
  65. package/dist/workers/forks.js +41 -35
  66. package/dist/workers/runVmTests.js +25 -22
  67. package/dist/workers/threads.js +41 -23
  68. package/dist/workers/vmForks.js +26 -39
  69. package/dist/workers/vmThreads.js +26 -29
  70. package/package.json +48 -35
  71. package/worker.d.ts +1 -0
  72. package/browser.d.ts +0 -1
  73. package/dist/chunks/base.BXI97p6t.js +0 -39
  74. package/dist/chunks/environment.d.2fYMoz3o.d.ts +0 -66
  75. package/dist/chunks/moduleTransport.I-bgQy0S.js +0 -19
  76. package/dist/chunks/resolver.Bx6lE0iq.js +0 -119
  77. package/dist/chunks/runBaseTests.D6sfuWBM.js +0 -99
  78. package/dist/chunks/typechecker.DSo_maXz.js +0 -762
  79. package/dist/chunks/utils.C2YI6McM.js +0 -52
  80. package/dist/chunks/worker.d.BKu8cnnX.d.ts +0 -8
  81. package/dist/workers.d.ts +0 -38
  82. package/dist/workers.js +0 -31
  83. package/execute.d.ts +0 -1
  84. package/utils.d.ts +0 -1
  85. package/workers.d.ts +0 -1
@@ -1,18 +1,22 @@
1
1
  import { existsSync, readFileSync, promises } from 'node:fs';
2
2
  import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
3
- import { resolve, dirname, isAbsolute, relative, basename, normalize } from 'pathe';
4
- import { g as getOutputFile, h as hasFailedSnapshot, T as TypeCheckError } from './typechecker.DSo_maXz.js';
3
+ import { resolve as resolve$1, dirname, isAbsolute, relative, basename, join, normalize } from 'pathe';
5
4
  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';
5
+ import { getTests, getTestName, hasFailed, getSuites, generateHash, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, getTasks, getFullName } from '@vitest/runner/utils';
6
+ import { slash, toArray, isPrimitive } from '@vitest/utils/helpers';
8
7
  import { parseStacktrace, parseErrorStacktrace, defaultStackIgnorePatterns } from '@vitest/utils/source-map';
9
8
  import c from 'tinyrainbow';
10
9
  import { i as isTTY } from './env.D4Lgay0q.js';
11
10
  import { stripVTControlCharacters } from 'node:util';
12
11
  import { Console } from 'node:console';
13
12
  import { Writable } from 'node:stream';
13
+ import { inspect } from '@vitest/utils/display';
14
+ import nodeos__default, { hostname } from 'node:os';
15
+ import { x } from 'tinyexec';
16
+ import { distDir } from '../path.js';
17
+ import { parseAstAsync } from 'vite';
18
+ import { positionToOffset, lineSplitRE } from '@vitest/utils/offset';
14
19
  import { createRequire } from 'node:module';
15
- import { hostname } from 'node:os';
16
20
 
17
21
  /// <reference types="../types/index.d.ts" />
18
22
 
@@ -24,7 +28,7 @@ const {keys} = Object;
24
28
  const Primitive = String; // it could be Number
25
29
  const primitive = 'string'; // it could be 'number'
26
30
 
27
- const ignore = {};
31
+ const ignore$1 = {};
28
32
  const object = 'object';
29
33
 
30
34
  const noop = (_, value) => value;
@@ -46,13 +50,13 @@ const revive = (input, parsed, output, $) => {
46
50
  const tmp = input[value];
47
51
  if (typeof tmp === object && !parsed.has(tmp)) {
48
52
  parsed.add(tmp);
49
- output[k] = ignore;
53
+ output[k] = ignore$1;
50
54
  lazy.push({k, a: [input, parsed, tmp, $]});
51
55
  }
52
56
  else
53
57
  output[k] = $.call(output, k, tmp);
54
58
  }
55
- else if (output[k] !== ignore)
59
+ else if (output[k] !== ignore$1)
56
60
  output[k] = $.call(output, k, value);
57
61
  }
58
62
  for (let {length} = lazy, i = 0; i < length; i++) {
@@ -74,7 +78,7 @@ const set = (known, input, value) => {
74
78
  * @param {(this: any, key: string, value: any) => any} [reviver]
75
79
  * @returns {any}
76
80
  */
77
- const parse = (text, reviver) => {
81
+ const parse$1 = (text, reviver) => {
78
82
  const input = $parse(text, Primitives).map(primitives);
79
83
  const value = input[0];
80
84
  const $ = reviver || noop;
@@ -121,6 +125,51 @@ const stringify = (value, replacer, space) => {
121
125
  }
122
126
  };
123
127
 
128
+ function getOutputFile(config, reporter) {
129
+ if (config?.outputFile) return typeof config.outputFile === "string" ? config.outputFile : config.outputFile[reporter];
130
+ }
131
+ function createDefinesScript(define) {
132
+ return !define || serializeDefine(define) === "{}" ? "" : `
133
+ const defines = ${serializeDefine(define)}
134
+ Object.keys(defines).forEach((key) => {
135
+ const segments = key.split('.')
136
+ let target = globalThis
137
+ for (let i = 0; i < segments.length; i++) {
138
+ const segment = segments[i]
139
+ if (i === segments.length - 1) {
140
+ target[segment] = defines[key]
141
+ } else {
142
+ target = target[segment] || (target[segment] = {})
143
+ }
144
+ }
145
+ })
146
+ `;
147
+ }
148
+ /**
149
+ * Like `JSON.stringify` but keeps raw string values as a literal
150
+ * in the generated code. For example: `"window"` would refer to
151
+ * the global `window` object directly.
152
+ */
153
+ function serializeDefine(define) {
154
+ const userDefine = {};
155
+ for (const key in define) {
156
+ // vitest sets this to avoid vite:client-inject plugin
157
+ if (key === "process.env.NODE_ENV" && define[key] === "process.env.NODE_ENV") continue;
158
+ // import.meta.env.* is handled in `importAnalysis` plugin
159
+ if (!key.startsWith("import.meta.env.")) userDefine[key] = define[key];
160
+ }
161
+ let res = `{`;
162
+ const keys = Object.keys(userDefine).sort();
163
+ for (let i = 0; i < keys.length; i++) {
164
+ const key = keys[i], val = userDefine[key];
165
+ if (res += `${JSON.stringify(key)}: ${handleDefineValue(val)}`, i !== keys.length - 1) res += `, `;
166
+ }
167
+ return `${res}}`;
168
+ }
169
+ function handleDefineValue(value) {
170
+ return typeof value === "undefined" ? "undefined" : typeof value === "string" ? value : JSON.stringify(value);
171
+ }
172
+
124
173
  class BlobReporter {
125
174
  start = 0;
126
175
  ctx;
@@ -158,7 +207,7 @@ class BlobReporter {
158
207
  modules,
159
208
  coverage,
160
209
  executionTime
161
- ], reportFile = resolve(this.ctx.config.root, outputFile);
210
+ ], reportFile = resolve$1(this.ctx.config.root, outputFile);
162
211
  await writeBlob(report, reportFile), this.ctx.logger.log("blob report written to", reportFile);
163
212
  }
164
213
  }
@@ -169,10 +218,10 @@ async function writeBlob(content, filename) {
169
218
  }
170
219
  async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
171
220
  // 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`);
175
- const content = await readFile(fullPath, "utf-8"), [version, files, errors, moduleKeys, coverage, executionTime] = parse(content);
221
+ const resolvedDir = resolve$1(process.cwd(), blobsDirectory), promises = (await readdir(resolvedDir)).map(async (filename) => {
222
+ const fullPath = resolve$1(resolvedDir, filename);
223
+ 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`);
224
+ const content = await readFile(fullPath, "utf-8"), [version, files, errors, moduleKeys, coverage, executionTime] = parse$1(content);
176
225
  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
226
  return {
178
227
  version,
@@ -214,6 +263,55 @@ async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
214
263
  };
215
264
  }
216
265
 
266
+ function hasFailedSnapshot(suite) {
267
+ return getTests(suite).some((s) => {
268
+ return s.result?.errors?.some((e) => typeof e?.message === "string" && e.message.match(/Snapshot .* mismatched/));
269
+ });
270
+ }
271
+ function convertTasksToEvents(file, onTask) {
272
+ const packs = [], events = [];
273
+ function visit(suite) {
274
+ onTask?.(suite), packs.push([
275
+ suite.id,
276
+ suite.result,
277
+ suite.meta
278
+ ]), events.push([
279
+ suite.id,
280
+ "suite-prepare",
281
+ void 0
282
+ ]), suite.tasks.forEach((task) => {
283
+ if (task.type === "suite") visit(task);
284
+ else if (onTask?.(task), suite.mode !== "skip" && suite.mode !== "todo") packs.push([
285
+ task.id,
286
+ task.result,
287
+ task.meta
288
+ ]), events.push([
289
+ task.id,
290
+ "test-prepare",
291
+ void 0
292
+ ]), task.annotations.forEach((annotation) => {
293
+ events.push([
294
+ task.id,
295
+ "test-annotation",
296
+ { annotation }
297
+ ]);
298
+ }), events.push([
299
+ task.id,
300
+ "test-finished",
301
+ void 0
302
+ ]);
303
+ }), events.push([
304
+ suite.id,
305
+ "suite-finished",
306
+ void 0
307
+ ]);
308
+ }
309
+ return visit(file), {
310
+ packs,
311
+ events
312
+ };
313
+ }
314
+
217
315
  const F_RIGHT = "→";
218
316
  const F_DOWN = "↓";
219
317
  const F_DOWN_RIGHT = "↳";
@@ -232,6 +330,7 @@ const testPass = c.green(F_CHECK);
232
330
  const taskFail = c.red(F_CROSS);
233
331
  const suiteFail = c.red(F_POINTER);
234
332
  const pending$1 = c.gray("·");
333
+ const separator = c.dim(" > ");
235
334
  const labelDefaultColors = [
236
335
  c.bgYellow,
237
336
  c.bgCyan,
@@ -351,6 +450,7 @@ var utils = /*#__PURE__*/Object.freeze({
351
450
  pending: pending$1,
352
451
  pointer: pointer,
353
452
  renderSnapshotSummary: renderSnapshotSummary,
453
+ separator: separator,
354
454
  skipped: skipped,
355
455
  suiteFail: suiteFail,
356
456
  taskFail: taskFail,
@@ -440,19 +540,10 @@ class BaseReporter {
440
540
  })), logs.forEach((log) => this.log(log));
441
541
  }
442
542
  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}`);
543
+ const testResult = test.result(), { duration = 0 } = test.diagnostic() || {}, padding = this.getTestIndentation(test.task), suffix = this.getTestCaseSuffix(test);
544
+ if (testResult.state === "failed") this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, separator)}`) + suffix);
545
+ else if (duration > this.ctx.config.slowTestThreshold) this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, separator)} ${suffix}`);
546
+ 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
547
  }
457
548
  getModuleLog(testModule, counts) {
458
549
  let state = c.dim(`${counts.tests} test${counts.tests > 1 ? "s" : ""}`);
@@ -461,46 +552,65 @@ class BaseReporter {
461
552
  let suffix = c.dim("(") + state + c.dim(")") + this.getDurationPrefix(testModule.task);
462
553
  const diagnostic = testModule.diagnostic();
463
554
  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}`;
555
+ return ` ${this.getEntityPrefix(testModule)} ${testModule.task.name} ${suffix}`;
468
556
  }
469
- printTestSuite(_suite) {
470
- // Suite name is included in getTestName by default
557
+ printTestSuite(testSuite) {
558
+ if (!this.renderSucceed) return;
559
+ const indentation = " ".repeat(getIndentation(testSuite.task)), tests = Array.from(testSuite.children.allTests()), state = this.getStateSymbol(testSuite);
560
+ this.log(` ${indentation}${state} ${testSuite.name} ${c.dim(`(${tests.length})`)}`);
471
561
  }
472
- getTestName(test, separator) {
473
- return getTestName(test, separator);
562
+ getTestName(test, _separator) {
563
+ return test.name;
474
564
  }
475
565
  getFullName(test, separator) {
476
- return getFullName(test, separator);
566
+ if (test === test.file) return test.name;
567
+ let name = test.file.name;
568
+ if (test.location) name += c.dim(`:${test.location.line}:${test.location.column}`);
569
+ return name += separator, name += getTestName(test, separator), name;
477
570
  }
478
- formatShortError(error) {
479
- return `${F_RIGHT} ${error.message}`;
480
- }
481
- getTestIndentation(_test) {
482
- return " ";
571
+ getTestIndentation(test) {
572
+ return " ".repeat(getIndentation(test));
483
573
  }
484
574
  printAnnotations(test, console, padding = 0) {
485
575
  const annotations = test.annotations();
486
576
  if (!annotations.length) return;
487
- const PADDING = " ".repeat(padding);
488
- annotations.forEach(({ location, type, message }) => {
577
+ const PADDING = " ".repeat(padding), groupedAnnotations = {};
578
+ for (const group in annotations.forEach((annotation) => {
579
+ const { location, type } = annotation;
580
+ let group;
489
581
  if (location) {
490
582
  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)}`);
583
+ group = `${c.gray(`${file}:${location.line}:${location.column}`)} ${c.bold(type)}`;
584
+ } else group = c.bold(type);
585
+ groupedAnnotations[group] ??= [], groupedAnnotations[group].push(annotation);
586
+ }), groupedAnnotations) this[console](`${PADDING}${c.blue(F_POINTER)} ${group}`), groupedAnnotations[group].forEach(({ message }) => {
493
587
  this[console](`${PADDING} ${c.blue(F_DOWN_RIGHT)} ${message}`);
494
588
  });
495
589
  }
590
+ getEntityPrefix(entity) {
591
+ let title = this.getStateSymbol(entity);
592
+ if (entity.project.name) title += ` ${formatProjectName(entity.project, "")}`;
593
+ if (entity.meta().typecheck) title += ` ${c.bgBlue(c.bold(" TS "))}`;
594
+ return title;
595
+ }
596
+ getTestCaseSuffix(testCase) {
597
+ const { heap, retryCount, repeatCount } = testCase.diagnostic() || {}, testResult = testCase.result();
598
+ let suffix = this.getDurationPrefix(testCase.task);
599
+ if (retryCount != null && retryCount > 0) suffix += c.yellow(` (retry x${retryCount})`);
600
+ if (repeatCount != null && repeatCount > 0) suffix += c.yellow(` (repeat x${repeatCount})`);
601
+ if (heap != null) suffix += c.magenta(` ${Math.floor(heap / 1024 / 1024)} MB heap used`);
602
+ if (testResult.state === "skipped" && testResult.note) suffix += c.dim(c.gray(` [${testResult.note}]`));
603
+ return suffix;
604
+ }
605
+ getStateSymbol(test) {
606
+ return getStateSymbol(test.task);
607
+ }
496
608
  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")}`);
609
+ const duration = task.result?.duration && Math.round(task.result?.duration);
610
+ return duration == null ? "" : (duration > this.ctx.config.slowTestThreshold ? c.yellow : c.green)(` ${duration}${c.dim("ms")}`);
500
611
  }
501
612
  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..."));
613
+ if (errors.length > 0 || hasFailed(files)) this.log(withLabel("red", "FAIL", "Tests failed. Watching for file changes..."));
504
614
  else if (this.ctx.isCancelling) this.log(withLabel("red", "CANCELLED", "Test run cancelled. Watching for file changes..."));
505
615
  else this.log(withLabel("green", "PASS", "Waiting for file changes..."));
506
616
  const hints = [c.dim("press ") + c.bold("h") + c.dim(" to show help")];
@@ -531,7 +641,7 @@ class BaseReporter {
531
641
  const output = log.type === "stdout" ? this.ctx.logger.outputStream : this.ctx.logger.errorStream, write = (msg) => output.write(msg);
532
642
  let headerText = "unknown test";
533
643
  const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0;
534
- if (task) headerText = this.getFullName(task, c.dim(" > "));
644
+ if (task) headerText = this.getFullName(task, separator);
535
645
  else if (log.taskId && log.taskId !== "__vitest__unknown_test__") headerText = log.taskId;
536
646
  if (write(c.gray(log.type + c.dim(` | ${headerText}\n`)) + log.content), log.origin) {
537
647
  // browser logs don't have an extra end of line at the end like Node.js does
@@ -550,8 +660,8 @@ class BaseReporter {
550
660
  shouldLog(log, taskState) {
551
661
  if (this.ctx.config.silent === true || this.ctx.config.silent === "passed-only" && taskState !== "failed") return false;
552
662
  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;
663
+ const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0, entity = task && this.ctx.state.getReportedEntity(task);
664
+ if (this.ctx.config.onConsoleLog(log.content, log.type, entity) === false) return false;
555
665
  }
556
666
  return true;
557
667
  }
@@ -601,12 +711,12 @@ class BaseReporter {
601
711
  if (errors.length) this.ctx.logger.printUnhandledErrors(errors), this.error();
602
712
  }
603
713
  reportBenchmarkSummary(files) {
604
- const benches = getTests(files), topBenches = benches.filter((i) => i.result?.benchmark?.rank === 1);
714
+ const topBenches = getTests(files).filter((i) => i.result?.benchmark?.rank === 1);
605
715
  this.log(`\n${withLabel("cyan", "BENCH", "Summary\n")}`);
606
716
  for (const bench of topBenches) {
607
717
  const group = bench.suite || bench.file;
608
718
  if (!group) continue;
609
- const groupName = this.getFullName(group, c.dim(" > ")), project = this.ctx.projects.find((p) => p.name === bench.file.projectName);
719
+ const groupName = this.getFullName(group, separator), project = this.ctx.projects.find((p) => p.name === bench.file.projectName);
610
720
  this.log(` ${formatProjectName(project)}${bench.name}${c.dim(` - ${groupName}`)}`);
611
721
  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
722
  for (const sibling of siblings) {
@@ -633,7 +743,7 @@ class BaseReporter {
633
743
  for (const [error, tasks] of errorsQueue) {
634
744
  for (const task of tasks) {
635
745
  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(" > "));
746
+ let name = this.getFullName(task, separator);
637
747
  if (filepath) name += c.dim(` [ ${this.relative(filepath)} ]`);
638
748
  this.ctx.logger.error(`${c.bgRed(c.bold(" FAIL "))} ${formatProjectName(project)}${name}`);
639
749
  }
@@ -664,6 +774,9 @@ function sum(items, cb) {
664
774
  return total + Math.max(cb(next) || 0, 0);
665
775
  }, 0);
666
776
  }
777
+ function getIndentation(suite, level = 1) {
778
+ return suite.suite && !("filepath" in suite.suite) ? getIndentation(suite.suite, level + 1) : level;
779
+ }
667
780
 
668
781
  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
782
  /**
@@ -876,11 +989,10 @@ class SummaryReporter {
876
989
  else if (state === "failed") this.modules.failed++;
877
990
  else if (module.task.mode === "todo" && state === "skipped") this.modules.todo++;
878
991
  else if (state === "skipped") this.modules.skipped++;
879
- const left = this.modules.total - this.modules.completed;
880
992
  // Keep finished tests visible in summary for a while if there are more tests left.
881
993
  // When a new test starts in onTestModuleQueued it will take this ones place.
882
994
  // This reduces flickering by making summary more stable.
883
- if (left > this.maxParallelTests) this.finishedModules.set(module.id, setTimeout(() => {
995
+ if (this.modules.total - this.modules.completed > this.maxParallelTests) this.finishedModules.set(module.id, setTimeout(() => {
884
996
  this.removeTestModule(module.id);
885
997
  }, FINISHED_TEST_CLEANUP_TIME_MS).unref());
886
998
  else
@@ -1097,6 +1209,1298 @@ function formatTests(states) {
1097
1209
  return output += currentIcon.color(currentIcon.char.repeat(count)), output;
1098
1210
  }
1099
1211
 
1212
+ // src/vlq.ts
1213
+ var comma = ",".charCodeAt(0);
1214
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1215
+ var intToChar = new Uint8Array(64);
1216
+ var charToInt = new Uint8Array(128);
1217
+ for (let i = 0; i < chars.length; i++) {
1218
+ const c = chars.charCodeAt(i);
1219
+ intToChar[i] = c;
1220
+ charToInt[c] = i;
1221
+ }
1222
+ function decodeInteger(reader, relative) {
1223
+ let value = 0;
1224
+ let shift = 0;
1225
+ let integer = 0;
1226
+ do {
1227
+ const c = reader.next();
1228
+ integer = charToInt[c];
1229
+ value |= (integer & 31) << shift;
1230
+ shift += 5;
1231
+ } while (integer & 32);
1232
+ const shouldNegate = value & 1;
1233
+ value >>>= 1;
1234
+ if (shouldNegate) {
1235
+ value = -2147483648 | -value;
1236
+ }
1237
+ return relative + value;
1238
+ }
1239
+ function hasMoreVlq(reader, max) {
1240
+ if (reader.pos >= max) return false;
1241
+ return reader.peek() !== comma;
1242
+ }
1243
+ var StringReader = class {
1244
+ constructor(buffer) {
1245
+ this.pos = 0;
1246
+ this.buffer = buffer;
1247
+ }
1248
+ next() {
1249
+ return this.buffer.charCodeAt(this.pos++);
1250
+ }
1251
+ peek() {
1252
+ return this.buffer.charCodeAt(this.pos);
1253
+ }
1254
+ indexOf(char) {
1255
+ const { buffer, pos } = this;
1256
+ const idx = buffer.indexOf(char, pos);
1257
+ return idx === -1 ? buffer.length : idx;
1258
+ }
1259
+ };
1260
+
1261
+ // src/sourcemap-codec.ts
1262
+ function decode(mappings) {
1263
+ const { length } = mappings;
1264
+ const reader = new StringReader(mappings);
1265
+ const decoded = [];
1266
+ let genColumn = 0;
1267
+ let sourcesIndex = 0;
1268
+ let sourceLine = 0;
1269
+ let sourceColumn = 0;
1270
+ let namesIndex = 0;
1271
+ do {
1272
+ const semi = reader.indexOf(";");
1273
+ const line = [];
1274
+ let sorted = true;
1275
+ let lastCol = 0;
1276
+ genColumn = 0;
1277
+ while (reader.pos < semi) {
1278
+ let seg;
1279
+ genColumn = decodeInteger(reader, genColumn);
1280
+ if (genColumn < lastCol) sorted = false;
1281
+ lastCol = genColumn;
1282
+ if (hasMoreVlq(reader, semi)) {
1283
+ sourcesIndex = decodeInteger(reader, sourcesIndex);
1284
+ sourceLine = decodeInteger(reader, sourceLine);
1285
+ sourceColumn = decodeInteger(reader, sourceColumn);
1286
+ if (hasMoreVlq(reader, semi)) {
1287
+ namesIndex = decodeInteger(reader, namesIndex);
1288
+ seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
1289
+ } else {
1290
+ seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
1291
+ }
1292
+ } else {
1293
+ seg = [genColumn];
1294
+ }
1295
+ line.push(seg);
1296
+ reader.pos++;
1297
+ }
1298
+ if (!sorted) sort(line);
1299
+ decoded.push(line);
1300
+ reader.pos = semi + 1;
1301
+ } while (reader.pos <= length);
1302
+ return decoded;
1303
+ }
1304
+ function sort(line) {
1305
+ line.sort(sortComparator$1);
1306
+ }
1307
+ function sortComparator$1(a, b) {
1308
+ return a[0] - b[0];
1309
+ }
1310
+
1311
+ // Matches the scheme of a URL, eg "http://"
1312
+ const schemeRegex = /^[\w+.-]+:\/\//;
1313
+ /**
1314
+ * Matches the parts of a URL:
1315
+ * 1. Scheme, including ":", guaranteed.
1316
+ * 2. User/password, including "@", optional.
1317
+ * 3. Host, guaranteed.
1318
+ * 4. Port, including ":", optional.
1319
+ * 5. Path, including "/", optional.
1320
+ * 6. Query, including "?", optional.
1321
+ * 7. Hash, including "#", optional.
1322
+ */
1323
+ const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
1324
+ /**
1325
+ * File URLs are weird. They dont' need the regular `//` in the scheme, they may or may not start
1326
+ * with a leading `/`, they can have a domain (but only if they don't start with a Windows drive).
1327
+ *
1328
+ * 1. Host, optional.
1329
+ * 2. Path, which may include "/", guaranteed.
1330
+ * 3. Query, including "?", optional.
1331
+ * 4. Hash, including "#", optional.
1332
+ */
1333
+ const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
1334
+ function isAbsoluteUrl(input) {
1335
+ return schemeRegex.test(input);
1336
+ }
1337
+ function isSchemeRelativeUrl(input) {
1338
+ return input.startsWith('//');
1339
+ }
1340
+ function isAbsolutePath(input) {
1341
+ return input.startsWith('/');
1342
+ }
1343
+ function isFileUrl(input) {
1344
+ return input.startsWith('file:');
1345
+ }
1346
+ function isRelative(input) {
1347
+ return /^[.?#]/.test(input);
1348
+ }
1349
+ function parseAbsoluteUrl(input) {
1350
+ const match = urlRegex.exec(input);
1351
+ return makeUrl(match[1], match[2] || '', match[3], match[4] || '', match[5] || '/', match[6] || '', match[7] || '');
1352
+ }
1353
+ function parseFileUrl(input) {
1354
+ const match = fileRegex.exec(input);
1355
+ const path = match[2];
1356
+ return makeUrl('file:', '', match[1] || '', '', isAbsolutePath(path) ? path : '/' + path, match[3] || '', match[4] || '');
1357
+ }
1358
+ function makeUrl(scheme, user, host, port, path, query, hash) {
1359
+ return {
1360
+ scheme,
1361
+ user,
1362
+ host,
1363
+ port,
1364
+ path,
1365
+ query,
1366
+ hash,
1367
+ type: 7 /* Absolute */,
1368
+ };
1369
+ }
1370
+ function parseUrl(input) {
1371
+ if (isSchemeRelativeUrl(input)) {
1372
+ const url = parseAbsoluteUrl('http:' + input);
1373
+ url.scheme = '';
1374
+ url.type = 6 /* SchemeRelative */;
1375
+ return url;
1376
+ }
1377
+ if (isAbsolutePath(input)) {
1378
+ const url = parseAbsoluteUrl('http://foo.com' + input);
1379
+ url.scheme = '';
1380
+ url.host = '';
1381
+ url.type = 5 /* AbsolutePath */;
1382
+ return url;
1383
+ }
1384
+ if (isFileUrl(input))
1385
+ return parseFileUrl(input);
1386
+ if (isAbsoluteUrl(input))
1387
+ return parseAbsoluteUrl(input);
1388
+ const url = parseAbsoluteUrl('http://foo.com/' + input);
1389
+ url.scheme = '';
1390
+ url.host = '';
1391
+ url.type = input
1392
+ ? input.startsWith('?')
1393
+ ? 3 /* Query */
1394
+ : input.startsWith('#')
1395
+ ? 2 /* Hash */
1396
+ : 4 /* RelativePath */
1397
+ : 1 /* Empty */;
1398
+ return url;
1399
+ }
1400
+ function stripPathFilename(path) {
1401
+ // If a path ends with a parent directory "..", then it's a relative path with excess parent
1402
+ // paths. It's not a file, so we can't strip it.
1403
+ if (path.endsWith('/..'))
1404
+ return path;
1405
+ const index = path.lastIndexOf('/');
1406
+ return path.slice(0, index + 1);
1407
+ }
1408
+ function mergePaths(url, base) {
1409
+ normalizePath(base, base.type);
1410
+ // If the path is just a "/", then it was an empty path to begin with (remember, we're a relative
1411
+ // path).
1412
+ if (url.path === '/') {
1413
+ url.path = base.path;
1414
+ }
1415
+ else {
1416
+ // Resolution happens relative to the base path's directory, not the file.
1417
+ url.path = stripPathFilename(base.path) + url.path;
1418
+ }
1419
+ }
1420
+ /**
1421
+ * The path can have empty directories "//", unneeded parents "foo/..", or current directory
1422
+ * "foo/.". We need to normalize to a standard representation.
1423
+ */
1424
+ function normalizePath(url, type) {
1425
+ const rel = type <= 4 /* RelativePath */;
1426
+ const pieces = url.path.split('/');
1427
+ // We need to preserve the first piece always, so that we output a leading slash. The item at
1428
+ // pieces[0] is an empty string.
1429
+ let pointer = 1;
1430
+ // Positive is the number of real directories we've output, used for popping a parent directory.
1431
+ // Eg, "foo/bar/.." will have a positive 2, and we can decrement to be left with just "foo".
1432
+ let positive = 0;
1433
+ // We need to keep a trailing slash if we encounter an empty directory (eg, splitting "foo/" will
1434
+ // generate `["foo", ""]` pieces). And, if we pop a parent directory. But once we encounter a
1435
+ // real directory, we won't need to append, unless the other conditions happen again.
1436
+ let addTrailingSlash = false;
1437
+ for (let i = 1; i < pieces.length; i++) {
1438
+ const piece = pieces[i];
1439
+ // An empty directory, could be a trailing slash, or just a double "//" in the path.
1440
+ if (!piece) {
1441
+ addTrailingSlash = true;
1442
+ continue;
1443
+ }
1444
+ // If we encounter a real directory, then we don't need to append anymore.
1445
+ addTrailingSlash = false;
1446
+ // A current directory, which we can always drop.
1447
+ if (piece === '.')
1448
+ continue;
1449
+ // A parent directory, we need to see if there are any real directories we can pop. Else, we
1450
+ // have an excess of parents, and we'll need to keep the "..".
1451
+ if (piece === '..') {
1452
+ if (positive) {
1453
+ addTrailingSlash = true;
1454
+ positive--;
1455
+ pointer--;
1456
+ }
1457
+ else if (rel) {
1458
+ // If we're in a relativePath, then we need to keep the excess parents. Else, in an absolute
1459
+ // URL, protocol relative URL, or an absolute path, we don't need to keep excess.
1460
+ pieces[pointer++] = piece;
1461
+ }
1462
+ continue;
1463
+ }
1464
+ // We've encountered a real directory. Move it to the next insertion pointer, which accounts for
1465
+ // any popped or dropped directories.
1466
+ pieces[pointer++] = piece;
1467
+ positive++;
1468
+ }
1469
+ let path = '';
1470
+ for (let i = 1; i < pointer; i++) {
1471
+ path += '/' + pieces[i];
1472
+ }
1473
+ if (!path || (addTrailingSlash && !path.endsWith('/..'))) {
1474
+ path += '/';
1475
+ }
1476
+ url.path = path;
1477
+ }
1478
+ /**
1479
+ * Attempts to resolve `input` URL/path relative to `base`.
1480
+ */
1481
+ function resolve(input, base) {
1482
+ if (!input && !base)
1483
+ return '';
1484
+ const url = parseUrl(input);
1485
+ let inputType = url.type;
1486
+ if (base && inputType !== 7 /* Absolute */) {
1487
+ const baseUrl = parseUrl(base);
1488
+ const baseType = baseUrl.type;
1489
+ switch (inputType) {
1490
+ case 1 /* Empty */:
1491
+ url.hash = baseUrl.hash;
1492
+ // fall through
1493
+ case 2 /* Hash */:
1494
+ url.query = baseUrl.query;
1495
+ // fall through
1496
+ case 3 /* Query */:
1497
+ case 4 /* RelativePath */:
1498
+ mergePaths(url, baseUrl);
1499
+ // fall through
1500
+ case 5 /* AbsolutePath */:
1501
+ // The host, user, and port are joined, you can't copy one without the others.
1502
+ url.user = baseUrl.user;
1503
+ url.host = baseUrl.host;
1504
+ url.port = baseUrl.port;
1505
+ // fall through
1506
+ case 6 /* SchemeRelative */:
1507
+ // The input doesn't have a schema at least, so we need to copy at least that over.
1508
+ url.scheme = baseUrl.scheme;
1509
+ }
1510
+ if (baseType > inputType)
1511
+ inputType = baseType;
1512
+ }
1513
+ normalizePath(url, inputType);
1514
+ const queryHash = url.query + url.hash;
1515
+ switch (inputType) {
1516
+ // This is impossible, because of the empty checks at the start of the function.
1517
+ // case UrlType.Empty:
1518
+ case 2 /* Hash */:
1519
+ case 3 /* Query */:
1520
+ return queryHash;
1521
+ case 4 /* RelativePath */: {
1522
+ // The first char is always a "/", and we need it to be relative.
1523
+ const path = url.path.slice(1);
1524
+ if (!path)
1525
+ return queryHash || '.';
1526
+ if (isRelative(base || input) && !isRelative(path)) {
1527
+ // If base started with a leading ".", or there is no base and input started with a ".",
1528
+ // then we need to ensure that the relative path starts with a ".". We don't know if
1529
+ // relative starts with a "..", though, so check before prepending.
1530
+ return './' + path + queryHash;
1531
+ }
1532
+ return path + queryHash;
1533
+ }
1534
+ case 5 /* AbsolutePath */:
1535
+ return url.path + queryHash;
1536
+ default:
1537
+ return url.scheme + '//' + url.user + url.host + url.port + url.path + queryHash;
1538
+ }
1539
+ }
1540
+
1541
+ // src/trace-mapping.ts
1542
+
1543
+ // src/strip-filename.ts
1544
+ function stripFilename(path) {
1545
+ if (!path) return "";
1546
+ const index = path.lastIndexOf("/");
1547
+ return path.slice(0, index + 1);
1548
+ }
1549
+
1550
+ // src/resolve.ts
1551
+ function resolver(mapUrl, sourceRoot) {
1552
+ const from = stripFilename(mapUrl);
1553
+ const prefix = sourceRoot ? sourceRoot + "/" : "";
1554
+ return (source) => resolve(prefix + (source || ""), from);
1555
+ }
1556
+
1557
+ // src/sourcemap-segment.ts
1558
+ var COLUMN = 0;
1559
+ var SOURCES_INDEX = 1;
1560
+ var SOURCE_LINE = 2;
1561
+ var SOURCE_COLUMN = 3;
1562
+ var NAMES_INDEX = 4;
1563
+ var REV_GENERATED_LINE = 1;
1564
+ var REV_GENERATED_COLUMN = 2;
1565
+
1566
+ // src/sort.ts
1567
+ function maybeSort(mappings, owned) {
1568
+ const unsortedIndex = nextUnsortedSegmentLine(mappings, 0);
1569
+ if (unsortedIndex === mappings.length) return mappings;
1570
+ if (!owned) mappings = mappings.slice();
1571
+ for (let i = unsortedIndex; i < mappings.length; i = nextUnsortedSegmentLine(mappings, i + 1)) {
1572
+ mappings[i] = sortSegments(mappings[i], owned);
1573
+ }
1574
+ return mappings;
1575
+ }
1576
+ function nextUnsortedSegmentLine(mappings, start) {
1577
+ for (let i = start; i < mappings.length; i++) {
1578
+ if (!isSorted(mappings[i])) return i;
1579
+ }
1580
+ return mappings.length;
1581
+ }
1582
+ function isSorted(line) {
1583
+ for (let j = 1; j < line.length; j++) {
1584
+ if (line[j][COLUMN] < line[j - 1][COLUMN]) {
1585
+ return false;
1586
+ }
1587
+ }
1588
+ return true;
1589
+ }
1590
+ function sortSegments(line, owned) {
1591
+ if (!owned) line = line.slice();
1592
+ return line.sort(sortComparator);
1593
+ }
1594
+ function sortComparator(a, b) {
1595
+ return a[COLUMN] - b[COLUMN];
1596
+ }
1597
+
1598
+ // src/by-source.ts
1599
+ function buildBySources(decoded, memos) {
1600
+ const sources = memos.map(() => []);
1601
+ for (let i = 0; i < decoded.length; i++) {
1602
+ const line = decoded[i];
1603
+ for (let j = 0; j < line.length; j++) {
1604
+ const seg = line[j];
1605
+ if (seg.length === 1) continue;
1606
+ const sourceIndex2 = seg[SOURCES_INDEX];
1607
+ const sourceLine = seg[SOURCE_LINE];
1608
+ const sourceColumn = seg[SOURCE_COLUMN];
1609
+ const source = sources[sourceIndex2];
1610
+ const segs = source[sourceLine] || (source[sourceLine] = []);
1611
+ segs.push([sourceColumn, i, seg[COLUMN]]);
1612
+ }
1613
+ }
1614
+ for (let i = 0; i < sources.length; i++) {
1615
+ const source = sources[i];
1616
+ for (let j = 0; j < source.length; j++) {
1617
+ const line = source[j];
1618
+ if (line) line.sort(sortComparator);
1619
+ }
1620
+ }
1621
+ return sources;
1622
+ }
1623
+
1624
+ // src/binary-search.ts
1625
+ var found = false;
1626
+ function binarySearch(haystack, needle, low, high) {
1627
+ while (low <= high) {
1628
+ const mid = low + (high - low >> 1);
1629
+ const cmp = haystack[mid][COLUMN] - needle;
1630
+ if (cmp === 0) {
1631
+ found = true;
1632
+ return mid;
1633
+ }
1634
+ if (cmp < 0) {
1635
+ low = mid + 1;
1636
+ } else {
1637
+ high = mid - 1;
1638
+ }
1639
+ }
1640
+ found = false;
1641
+ return low - 1;
1642
+ }
1643
+ function upperBound(haystack, needle, index) {
1644
+ for (let i = index + 1; i < haystack.length; index = i++) {
1645
+ if (haystack[i][COLUMN] !== needle) break;
1646
+ }
1647
+ return index;
1648
+ }
1649
+ function lowerBound(haystack, needle, index) {
1650
+ for (let i = index - 1; i >= 0; index = i--) {
1651
+ if (haystack[i][COLUMN] !== needle) break;
1652
+ }
1653
+ return index;
1654
+ }
1655
+ function memoizedState() {
1656
+ return {
1657
+ lastKey: -1,
1658
+ lastNeedle: -1,
1659
+ lastIndex: -1
1660
+ };
1661
+ }
1662
+ function memoizedBinarySearch(haystack, needle, state, key) {
1663
+ const { lastKey, lastNeedle, lastIndex } = state;
1664
+ let low = 0;
1665
+ let high = haystack.length - 1;
1666
+ if (key === lastKey) {
1667
+ if (needle === lastNeedle) {
1668
+ found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
1669
+ return lastIndex;
1670
+ }
1671
+ if (needle >= lastNeedle) {
1672
+ low = lastIndex === -1 ? 0 : lastIndex;
1673
+ } else {
1674
+ high = lastIndex;
1675
+ }
1676
+ }
1677
+ state.lastKey = key;
1678
+ state.lastNeedle = needle;
1679
+ return state.lastIndex = binarySearch(haystack, needle, low, high);
1680
+ }
1681
+
1682
+ // src/types.ts
1683
+ function parse(map) {
1684
+ return typeof map === "string" ? JSON.parse(map) : map;
1685
+ }
1686
+
1687
+ // src/trace-mapping.ts
1688
+ var LINE_GTR_ZERO = "`line` must be greater than 0 (lines start at line 1)";
1689
+ var COL_GTR_EQ_ZERO = "`column` must be greater than or equal to 0 (columns start at column 0)";
1690
+ var LEAST_UPPER_BOUND = -1;
1691
+ var GREATEST_LOWER_BOUND = 1;
1692
+ var TraceMap = class {
1693
+ constructor(map, mapUrl) {
1694
+ const isString = typeof map === "string";
1695
+ if (!isString && map._decodedMemo) return map;
1696
+ const parsed = parse(map);
1697
+ const { version, file, names, sourceRoot, sources, sourcesContent } = parsed;
1698
+ this.version = version;
1699
+ this.file = file;
1700
+ this.names = names || [];
1701
+ this.sourceRoot = sourceRoot;
1702
+ this.sources = sources;
1703
+ this.sourcesContent = sourcesContent;
1704
+ this.ignoreList = parsed.ignoreList || parsed.x_google_ignoreList || void 0;
1705
+ const resolve = resolver(mapUrl, sourceRoot);
1706
+ this.resolvedSources = sources.map(resolve);
1707
+ const { mappings } = parsed;
1708
+ if (typeof mappings === "string") {
1709
+ this._encoded = mappings;
1710
+ this._decoded = void 0;
1711
+ } else if (Array.isArray(mappings)) {
1712
+ this._encoded = void 0;
1713
+ this._decoded = maybeSort(mappings, isString);
1714
+ } else if (parsed.sections) {
1715
+ throw new Error(`TraceMap passed sectioned source map, please use FlattenMap export instead`);
1716
+ } else {
1717
+ throw new Error(`invalid source map: ${JSON.stringify(parsed)}`);
1718
+ }
1719
+ this._decodedMemo = memoizedState();
1720
+ this._bySources = void 0;
1721
+ this._bySourceMemos = void 0;
1722
+ }
1723
+ };
1724
+ function cast(map) {
1725
+ return map;
1726
+ }
1727
+ function decodedMappings(map) {
1728
+ var _a;
1729
+ return (_a = cast(map))._decoded || (_a._decoded = decode(cast(map)._encoded));
1730
+ }
1731
+ function originalPositionFor(map, needle) {
1732
+ let { line, column, bias } = needle;
1733
+ line--;
1734
+ if (line < 0) throw new Error(LINE_GTR_ZERO);
1735
+ if (column < 0) throw new Error(COL_GTR_EQ_ZERO);
1736
+ const decoded = decodedMappings(map);
1737
+ if (line >= decoded.length) return OMapping(null, null, null, null);
1738
+ const segments = decoded[line];
1739
+ const index = traceSegmentInternal(
1740
+ segments,
1741
+ cast(map)._decodedMemo,
1742
+ line,
1743
+ column,
1744
+ bias || GREATEST_LOWER_BOUND
1745
+ );
1746
+ if (index === -1) return OMapping(null, null, null, null);
1747
+ const segment = segments[index];
1748
+ if (segment.length === 1) return OMapping(null, null, null, null);
1749
+ const { names, resolvedSources } = map;
1750
+ return OMapping(
1751
+ resolvedSources[segment[SOURCES_INDEX]],
1752
+ segment[SOURCE_LINE] + 1,
1753
+ segment[SOURCE_COLUMN],
1754
+ segment.length === 5 ? names[segment[NAMES_INDEX]] : null
1755
+ );
1756
+ }
1757
+ function generatedPositionFor(map, needle) {
1758
+ const { source, line, column, bias } = needle;
1759
+ return generatedPosition(map, source, line, column, bias || GREATEST_LOWER_BOUND, false);
1760
+ }
1761
+ function eachMapping(map, cb) {
1762
+ const decoded = decodedMappings(map);
1763
+ const { names, resolvedSources } = map;
1764
+ for (let i = 0; i < decoded.length; i++) {
1765
+ const line = decoded[i];
1766
+ for (let j = 0; j < line.length; j++) {
1767
+ const seg = line[j];
1768
+ const generatedLine = i + 1;
1769
+ const generatedColumn = seg[0];
1770
+ let source = null;
1771
+ let originalLine = null;
1772
+ let originalColumn = null;
1773
+ let name = null;
1774
+ if (seg.length !== 1) {
1775
+ source = resolvedSources[seg[1]];
1776
+ originalLine = seg[2] + 1;
1777
+ originalColumn = seg[3];
1778
+ }
1779
+ if (seg.length === 5) name = names[seg[4]];
1780
+ cb({
1781
+ generatedLine,
1782
+ generatedColumn,
1783
+ source,
1784
+ originalLine,
1785
+ originalColumn,
1786
+ name
1787
+ });
1788
+ }
1789
+ }
1790
+ }
1791
+ function OMapping(source, line, column, name) {
1792
+ return { source, line, column, name };
1793
+ }
1794
+ function GMapping(line, column) {
1795
+ return { line, column };
1796
+ }
1797
+ function traceSegmentInternal(segments, memo, line, column, bias) {
1798
+ let index = memoizedBinarySearch(segments, column, memo, line);
1799
+ if (found) {
1800
+ index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index);
1801
+ } else if (bias === LEAST_UPPER_BOUND) index++;
1802
+ if (index === -1 || index === segments.length) return -1;
1803
+ return index;
1804
+ }
1805
+ function generatedPosition(map, source, line, column, bias, all) {
1806
+ var _a, _b;
1807
+ line--;
1808
+ if (line < 0) throw new Error(LINE_GTR_ZERO);
1809
+ if (column < 0) throw new Error(COL_GTR_EQ_ZERO);
1810
+ const { sources, resolvedSources } = map;
1811
+ let sourceIndex2 = sources.indexOf(source);
1812
+ if (sourceIndex2 === -1) sourceIndex2 = resolvedSources.indexOf(source);
1813
+ if (sourceIndex2 === -1) return all ? [] : GMapping(null, null);
1814
+ const bySourceMemos = (_a = cast(map))._bySourceMemos || (_a._bySourceMemos = sources.map(memoizedState));
1815
+ const generated = (_b = cast(map))._bySources || (_b._bySources = buildBySources(decodedMappings(map), bySourceMemos));
1816
+ const segments = generated[sourceIndex2][line];
1817
+ if (segments == null) return all ? [] : GMapping(null, null);
1818
+ const memo = bySourceMemos[sourceIndex2];
1819
+ const index = traceSegmentInternal(segments, memo, line, column, bias);
1820
+ if (index === -1) return GMapping(null, null);
1821
+ const segment = segments[index];
1822
+ return GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN]);
1823
+ }
1824
+
1825
+ // AST walker module for ESTree compatible trees
1826
+
1827
+
1828
+ // An ancestor walk keeps an array of ancestor nodes (including the
1829
+ // current node) and passes them to the callback as third parameter
1830
+ // (and also as state parameter when no other state is present).
1831
+ function ancestor(node, visitors, baseVisitor, state, override) {
1832
+ var ancestors = [];
1833
+ if (!baseVisitor) { baseVisitor = base
1834
+ ; }(function c(node, st, override) {
1835
+ var type = override || node.type;
1836
+ var isNew = node !== ancestors[ancestors.length - 1];
1837
+ if (isNew) { ancestors.push(node); }
1838
+ baseVisitor[type](node, st, c);
1839
+ if (visitors[type]) { visitors[type](node, st || ancestors, ancestors); }
1840
+ if (isNew) { ancestors.pop(); }
1841
+ })(node, state, override);
1842
+ }
1843
+
1844
+ function skipThrough(node, st, c) { c(node, st); }
1845
+ function ignore(_node, _st, _c) {}
1846
+
1847
+ // Node walkers.
1848
+
1849
+ var base = {};
1850
+
1851
+ base.Program = base.BlockStatement = base.StaticBlock = function (node, st, c) {
1852
+ for (var i = 0, list = node.body; i < list.length; i += 1)
1853
+ {
1854
+ var stmt = list[i];
1855
+
1856
+ c(stmt, st, "Statement");
1857
+ }
1858
+ };
1859
+ base.Statement = skipThrough;
1860
+ base.EmptyStatement = ignore;
1861
+ base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression =
1862
+ function (node, st, c) { return c(node.expression, st, "Expression"); };
1863
+ base.IfStatement = function (node, st, c) {
1864
+ c(node.test, st, "Expression");
1865
+ c(node.consequent, st, "Statement");
1866
+ if (node.alternate) { c(node.alternate, st, "Statement"); }
1867
+ };
1868
+ base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };
1869
+ base.BreakStatement = base.ContinueStatement = ignore;
1870
+ base.WithStatement = function (node, st, c) {
1871
+ c(node.object, st, "Expression");
1872
+ c(node.body, st, "Statement");
1873
+ };
1874
+ base.SwitchStatement = function (node, st, c) {
1875
+ c(node.discriminant, st, "Expression");
1876
+ for (var i = 0, list = node.cases; i < list.length; i += 1) {
1877
+ var cs = list[i];
1878
+
1879
+ c(cs, st);
1880
+ }
1881
+ };
1882
+ base.SwitchCase = function (node, st, c) {
1883
+ if (node.test) { c(node.test, st, "Expression"); }
1884
+ for (var i = 0, list = node.consequent; i < list.length; i += 1)
1885
+ {
1886
+ var cons = list[i];
1887
+
1888
+ c(cons, st, "Statement");
1889
+ }
1890
+ };
1891
+ base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {
1892
+ if (node.argument) { c(node.argument, st, "Expression"); }
1893
+ };
1894
+ base.ThrowStatement = base.SpreadElement =
1895
+ function (node, st, c) { return c(node.argument, st, "Expression"); };
1896
+ base.TryStatement = function (node, st, c) {
1897
+ c(node.block, st, "Statement");
1898
+ if (node.handler) { c(node.handler, st); }
1899
+ if (node.finalizer) { c(node.finalizer, st, "Statement"); }
1900
+ };
1901
+ base.CatchClause = function (node, st, c) {
1902
+ if (node.param) { c(node.param, st, "Pattern"); }
1903
+ c(node.body, st, "Statement");
1904
+ };
1905
+ base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
1906
+ c(node.test, st, "Expression");
1907
+ c(node.body, st, "Statement");
1908
+ };
1909
+ base.ForStatement = function (node, st, c) {
1910
+ if (node.init) { c(node.init, st, "ForInit"); }
1911
+ if (node.test) { c(node.test, st, "Expression"); }
1912
+ if (node.update) { c(node.update, st, "Expression"); }
1913
+ c(node.body, st, "Statement");
1914
+ };
1915
+ base.ForInStatement = base.ForOfStatement = function (node, st, c) {
1916
+ c(node.left, st, "ForInit");
1917
+ c(node.right, st, "Expression");
1918
+ c(node.body, st, "Statement");
1919
+ };
1920
+ base.ForInit = function (node, st, c) {
1921
+ if (node.type === "VariableDeclaration") { c(node, st); }
1922
+ else { c(node, st, "Expression"); }
1923
+ };
1924
+ base.DebuggerStatement = ignore;
1925
+
1926
+ base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };
1927
+ base.VariableDeclaration = function (node, st, c) {
1928
+ for (var i = 0, list = node.declarations; i < list.length; i += 1)
1929
+ {
1930
+ var decl = list[i];
1931
+
1932
+ c(decl, st);
1933
+ }
1934
+ };
1935
+ base.VariableDeclarator = function (node, st, c) {
1936
+ c(node.id, st, "Pattern");
1937
+ if (node.init) { c(node.init, st, "Expression"); }
1938
+ };
1939
+
1940
+ base.Function = function (node, st, c) {
1941
+ if (node.id) { c(node.id, st, "Pattern"); }
1942
+ for (var i = 0, list = node.params; i < list.length; i += 1)
1943
+ {
1944
+ var param = list[i];
1945
+
1946
+ c(param, st, "Pattern");
1947
+ }
1948
+ c(node.body, st, node.expression ? "Expression" : "Statement");
1949
+ };
1950
+
1951
+ base.Pattern = function (node, st, c) {
1952
+ if (node.type === "Identifier")
1953
+ { c(node, st, "VariablePattern"); }
1954
+ else if (node.type === "MemberExpression")
1955
+ { c(node, st, "MemberPattern"); }
1956
+ else
1957
+ { c(node, st); }
1958
+ };
1959
+ base.VariablePattern = ignore;
1960
+ base.MemberPattern = skipThrough;
1961
+ base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };
1962
+ base.ArrayPattern = function (node, st, c) {
1963
+ for (var i = 0, list = node.elements; i < list.length; i += 1) {
1964
+ var elt = list[i];
1965
+
1966
+ if (elt) { c(elt, st, "Pattern"); }
1967
+ }
1968
+ };
1969
+ base.ObjectPattern = function (node, st, c) {
1970
+ for (var i = 0, list = node.properties; i < list.length; i += 1) {
1971
+ var prop = list[i];
1972
+
1973
+ if (prop.type === "Property") {
1974
+ if (prop.computed) { c(prop.key, st, "Expression"); }
1975
+ c(prop.value, st, "Pattern");
1976
+ } else if (prop.type === "RestElement") {
1977
+ c(prop.argument, st, "Pattern");
1978
+ }
1979
+ }
1980
+ };
1981
+
1982
+ base.Expression = skipThrough;
1983
+ base.ThisExpression = base.Super = base.MetaProperty = ignore;
1984
+ base.ArrayExpression = function (node, st, c) {
1985
+ for (var i = 0, list = node.elements; i < list.length; i += 1) {
1986
+ var elt = list[i];
1987
+
1988
+ if (elt) { c(elt, st, "Expression"); }
1989
+ }
1990
+ };
1991
+ base.ObjectExpression = function (node, st, c) {
1992
+ for (var i = 0, list = node.properties; i < list.length; i += 1)
1993
+ {
1994
+ var prop = list[i];
1995
+
1996
+ c(prop, st);
1997
+ }
1998
+ };
1999
+ base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
2000
+ base.SequenceExpression = function (node, st, c) {
2001
+ for (var i = 0, list = node.expressions; i < list.length; i += 1)
2002
+ {
2003
+ var expr = list[i];
2004
+
2005
+ c(expr, st, "Expression");
2006
+ }
2007
+ };
2008
+ base.TemplateLiteral = function (node, st, c) {
2009
+ for (var i = 0, list = node.quasis; i < list.length; i += 1)
2010
+ {
2011
+ var quasi = list[i];
2012
+
2013
+ c(quasi, st);
2014
+ }
2015
+
2016
+ for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1)
2017
+ {
2018
+ var expr = list$1[i$1];
2019
+
2020
+ c(expr, st, "Expression");
2021
+ }
2022
+ };
2023
+ base.TemplateElement = ignore;
2024
+ base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
2025
+ c(node.argument, st, "Expression");
2026
+ };
2027
+ base.BinaryExpression = base.LogicalExpression = function (node, st, c) {
2028
+ c(node.left, st, "Expression");
2029
+ c(node.right, st, "Expression");
2030
+ };
2031
+ base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {
2032
+ c(node.left, st, "Pattern");
2033
+ c(node.right, st, "Expression");
2034
+ };
2035
+ base.ConditionalExpression = function (node, st, c) {
2036
+ c(node.test, st, "Expression");
2037
+ c(node.consequent, st, "Expression");
2038
+ c(node.alternate, st, "Expression");
2039
+ };
2040
+ base.NewExpression = base.CallExpression = function (node, st, c) {
2041
+ c(node.callee, st, "Expression");
2042
+ if (node.arguments)
2043
+ { for (var i = 0, list = node.arguments; i < list.length; i += 1)
2044
+ {
2045
+ var arg = list[i];
2046
+
2047
+ c(arg, st, "Expression");
2048
+ } }
2049
+ };
2050
+ base.MemberExpression = function (node, st, c) {
2051
+ c(node.object, st, "Expression");
2052
+ if (node.computed) { c(node.property, st, "Expression"); }
2053
+ };
2054
+ base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
2055
+ if (node.declaration)
2056
+ { c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }
2057
+ if (node.source) { c(node.source, st, "Expression"); }
2058
+ };
2059
+ base.ExportAllDeclaration = function (node, st, c) {
2060
+ if (node.exported)
2061
+ { c(node.exported, st); }
2062
+ c(node.source, st, "Expression");
2063
+ };
2064
+ base.ImportDeclaration = function (node, st, c) {
2065
+ for (var i = 0, list = node.specifiers; i < list.length; i += 1)
2066
+ {
2067
+ var spec = list[i];
2068
+
2069
+ c(spec, st);
2070
+ }
2071
+ c(node.source, st, "Expression");
2072
+ };
2073
+ base.ImportExpression = function (node, st, c) {
2074
+ c(node.source, st, "Expression");
2075
+ };
2076
+ base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore;
2077
+
2078
+ base.TaggedTemplateExpression = function (node, st, c) {
2079
+ c(node.tag, st, "Expression");
2080
+ c(node.quasi, st, "Expression");
2081
+ };
2082
+ base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };
2083
+ base.Class = function (node, st, c) {
2084
+ if (node.id) { c(node.id, st, "Pattern"); }
2085
+ if (node.superClass) { c(node.superClass, st, "Expression"); }
2086
+ c(node.body, st);
2087
+ };
2088
+ base.ClassBody = function (node, st, c) {
2089
+ for (var i = 0, list = node.body; i < list.length; i += 1)
2090
+ {
2091
+ var elt = list[i];
2092
+
2093
+ c(elt, st);
2094
+ }
2095
+ };
2096
+ base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) {
2097
+ if (node.computed) { c(node.key, st, "Expression"); }
2098
+ if (node.value) { c(node.value, st, "Expression"); }
2099
+ };
2100
+
2101
+ async function collectTests(ctx, filepath) {
2102
+ const request = await ctx.vite.environments.ssr.transformRequest(filepath);
2103
+ if (!request) return null;
2104
+ const ast = await parseAstAsync(request.code), testFilepath = relative(ctx.config.root, filepath), projectName = ctx.name, typecheckSubprojectName = projectName ? `${projectName}:__typecheck__` : "__typecheck__", file = {
2105
+ filepath,
2106
+ type: "suite",
2107
+ id: generateHash(`${testFilepath}${typecheckSubprojectName}`),
2108
+ name: testFilepath,
2109
+ mode: "run",
2110
+ tasks: [],
2111
+ start: ast.start,
2112
+ end: ast.end,
2113
+ projectName,
2114
+ meta: { typecheck: true },
2115
+ file: null
2116
+ };
2117
+ file.file = file;
2118
+ const definitions = [], getName = (callee) => {
2119
+ if (!callee) return null;
2120
+ if (callee.type === "Identifier") return callee.name;
2121
+ if (callee.type === "CallExpression") return getName(callee.callee);
2122
+ if (callee.type === "TaggedTemplateExpression") return getName(callee.tag);
2123
+ if (callee.type === "MemberExpression")
2124
+ // call as `__vite_ssr__.test.skip()`
2125
+ return callee.object?.type === "Identifier" && [
2126
+ "it",
2127
+ "test",
2128
+ "describe",
2129
+ "suite"
2130
+ ].includes(callee.object.name) ? callee.object?.name : callee.object?.name?.startsWith("__vite_ssr_") ? getName(callee.property) : getName(callee.object?.property);
2131
+ // unwrap (0, ...)
2132
+ if (callee.type === "SequenceExpression" && callee.expressions.length === 2) {
2133
+ const [e0, e1] = callee.expressions;
2134
+ if (e0.type === "Literal" && e0.value === 0) return getName(e1);
2135
+ }
2136
+ return null;
2137
+ };
2138
+ ancestor(ast, { CallExpression(node) {
2139
+ const { callee } = node, name = getName(callee);
2140
+ if (!name || ![
2141
+ "it",
2142
+ "test",
2143
+ "describe",
2144
+ "suite"
2145
+ ].includes(name)) return;
2146
+ const property = callee?.property?.name;
2147
+ let mode = !property || property === name ? "run" : property;
2148
+ // they will be picked up in the next iteration
2149
+ if ([
2150
+ "each",
2151
+ "for",
2152
+ "skipIf",
2153
+ "runIf"
2154
+ ].includes(mode)) return;
2155
+ let start;
2156
+ const end = node.end;
2157
+ // .each
2158
+ if (callee.type === "CallExpression") start = callee.end;
2159
+ else if (callee.type === "TaggedTemplateExpression") start = callee.end + 1;
2160
+ else start = node.start;
2161
+ const { arguments: [messageNode] } = node, message = messageNode?.type === "Literal" || messageNode?.type === "TemplateLiteral" ? request.code.slice(messageNode.start + 1, messageNode.end - 1) : request.code.slice(messageNode.start, messageNode.end);
2162
+ // cannot statically analyze, so we always skip it
2163
+ if (mode === "skipIf" || mode === "runIf") mode = "skip";
2164
+ definitions.push({
2165
+ start,
2166
+ end,
2167
+ name: message,
2168
+ type: name === "it" || name === "test" ? "test" : "suite",
2169
+ mode,
2170
+ task: null
2171
+ });
2172
+ } });
2173
+ let lastSuite = file;
2174
+ const updateLatestSuite = (index) => {
2175
+ while (lastSuite.suite && lastSuite.end < index) lastSuite = lastSuite.suite;
2176
+ return lastSuite;
2177
+ };
2178
+ definitions.sort((a, b) => a.start - b.start).forEach((definition) => {
2179
+ const latestSuite = updateLatestSuite(definition.start);
2180
+ let mode = definition.mode;
2181
+ if (latestSuite.mode !== "run")
2182
+ // inherit suite mode, if it's set
2183
+ mode = latestSuite.mode;
2184
+ if (definition.type === "suite") {
2185
+ const task = {
2186
+ type: definition.type,
2187
+ id: "",
2188
+ suite: latestSuite,
2189
+ file,
2190
+ tasks: [],
2191
+ mode,
2192
+ name: definition.name,
2193
+ end: definition.end,
2194
+ start: definition.start,
2195
+ meta: { typecheck: true }
2196
+ };
2197
+ definition.task = task, latestSuite.tasks.push(task), lastSuite = task;
2198
+ return;
2199
+ }
2200
+ const task = {
2201
+ type: definition.type,
2202
+ id: "",
2203
+ suite: latestSuite,
2204
+ file,
2205
+ mode,
2206
+ timeout: 0,
2207
+ context: {},
2208
+ name: definition.name,
2209
+ end: definition.end,
2210
+ start: definition.start,
2211
+ annotations: [],
2212
+ meta: { typecheck: true }
2213
+ };
2214
+ definition.task = task, latestSuite.tasks.push(task);
2215
+ }), calculateSuiteHash(file);
2216
+ const hasOnly = someTasksAreOnly(file);
2217
+ return interpretTaskModes(file, ctx.config.testNamePattern, void 0, hasOnly, false, ctx.config.allowOnly), {
2218
+ file,
2219
+ parsed: request.code,
2220
+ filepath,
2221
+ map: request.map,
2222
+ definitions
2223
+ };
2224
+ }
2225
+
2226
+ const newLineRegExp = /\r?\n/, errCodeRegExp = /error TS(?<errCode>\d+)/;
2227
+ async function makeTscErrorInfo(errInfo) {
2228
+ const [errFilePathPos = "", ...errMsgRawArr] = errInfo.split(":");
2229
+ if (!errFilePathPos || errMsgRawArr.length === 0 || errMsgRawArr.join("").length === 0) return ["unknown filepath", null];
2230
+ const errMsgRaw = errMsgRawArr.join("").trim(), [errFilePath, errPos] = errFilePathPos.slice(0, -1).split("(");
2231
+ if (!errFilePath || !errPos) return ["unknown filepath", null];
2232
+ const [errLine, errCol] = errPos.split(",");
2233
+ if (!errLine || !errCol) return [errFilePath, null];
2234
+ // get errCode, errMsg
2235
+ const execArr = errCodeRegExp.exec(errMsgRaw);
2236
+ if (!execArr) return [errFilePath, null];
2237
+ const errCodeStr = execArr.groups?.errCode ?? "";
2238
+ if (!errCodeStr) return [errFilePath, null];
2239
+ const line = Number(errLine), col = Number(errCol), errCode = Number(errCodeStr);
2240
+ return [errFilePath, {
2241
+ filePath: errFilePath,
2242
+ errCode,
2243
+ line,
2244
+ column: col,
2245
+ errMsg: errMsgRaw.slice(`error TS${errCode} `.length)
2246
+ }];
2247
+ }
2248
+ async function getRawErrsMapFromTsCompile(tscErrorStdout) {
2249
+ const rawErrsMap = /* @__PURE__ */ new Map();
2250
+ return (await Promise.all(tscErrorStdout.split(newLineRegExp).reduce((prev, next) => {
2251
+ if (!next) return prev;
2252
+ if (next[0] !== " ") prev.push(next);
2253
+ else prev[prev.length - 1] += `\n${next}`;
2254
+ return prev;
2255
+ }, []).map((errInfoLine) => makeTscErrorInfo(errInfoLine)))).forEach(([errFilePath, errInfo]) => {
2256
+ if (errInfo) if (!rawErrsMap.has(errFilePath)) rawErrsMap.set(errFilePath, [errInfo]);
2257
+ else rawErrsMap.get(errFilePath)?.push(errInfo);
2258
+ }), rawErrsMap;
2259
+ }
2260
+
2261
+ function createIndexMap(source) {
2262
+ const map = /* @__PURE__ */ new Map();
2263
+ let index = 0, line = 1, column = 1;
2264
+ for (const char of source) if (map.set(`${line}:${column}`, index++), char === "\n" || char === "\r\n") line++, column = 0;
2265
+ else column++;
2266
+ return map;
2267
+ }
2268
+
2269
+ class TypeCheckError extends Error {
2270
+ name = "TypeCheckError";
2271
+ constructor(message, stacks) {
2272
+ super(message), this.message = message, this.stacks = stacks;
2273
+ }
2274
+ }
2275
+ class Typechecker {
2276
+ _onParseStart;
2277
+ _onParseEnd;
2278
+ _onWatcherRerun;
2279
+ _result = {
2280
+ files: [],
2281
+ sourceErrors: [],
2282
+ time: 0
2283
+ };
2284
+ _startTime = 0;
2285
+ _output = "";
2286
+ _tests = {};
2287
+ process;
2288
+ files = [];
2289
+ constructor(project) {
2290
+ this.project = project;
2291
+ }
2292
+ setFiles(files) {
2293
+ this.files = files;
2294
+ }
2295
+ onParseStart(fn) {
2296
+ this._onParseStart = fn;
2297
+ }
2298
+ onParseEnd(fn) {
2299
+ this._onParseEnd = fn;
2300
+ }
2301
+ onWatcherRerun(fn) {
2302
+ this._onWatcherRerun = fn;
2303
+ }
2304
+ async collectFileTests(filepath) {
2305
+ return collectTests(this.project, filepath);
2306
+ }
2307
+ getFiles() {
2308
+ return this.files;
2309
+ }
2310
+ async collectTests() {
2311
+ const tests = (await Promise.all(this.getFiles().map((filepath) => this.collectFileTests(filepath)))).reduce((acc, data) => {
2312
+ return data && (acc[data.filepath] = data), acc;
2313
+ }, {});
2314
+ return this._tests = tests, tests;
2315
+ }
2316
+ markPassed(file) {
2317
+ if (!file.result?.state) file.result = { state: "pass" };
2318
+ const markTasks = (tasks) => {
2319
+ for (const task of tasks) {
2320
+ if ("tasks" in task) markTasks(task.tasks);
2321
+ if (!task.result?.state && (task.mode === "run" || task.mode === "queued")) task.result = { state: "pass" };
2322
+ }
2323
+ };
2324
+ markTasks(file.tasks);
2325
+ }
2326
+ async prepareResults(output) {
2327
+ const typeErrors = await this.parseTscLikeOutput(output), testFiles = new Set(this.getFiles());
2328
+ if (!this._tests) this._tests = await this.collectTests();
2329
+ const sourceErrors = [], files = [];
2330
+ return testFiles.forEach((path) => {
2331
+ const { file, definitions, map, parsed } = this._tests[path], errors = typeErrors.get(path);
2332
+ if (files.push(file), !errors) {
2333
+ this.markPassed(file);
2334
+ return;
2335
+ }
2336
+ const sortedDefinitions = [...definitions.sort((a, b) => b.start - a.start)], traceMap = map && new TraceMap(map), indexMap = createIndexMap(parsed), markState = (task, state) => {
2337
+ if (task.result = { state: task.mode === "run" || task.mode === "only" ? state : task.mode }, task.suite) markState(task.suite, state);
2338
+ else if (task.file && task !== task.file) markState(task.file, state);
2339
+ };
2340
+ errors.forEach(({ error, originalError }) => {
2341
+ const processedPos = traceMap ? findGeneratedPosition(traceMap, {
2342
+ line: originalError.line,
2343
+ column: originalError.column,
2344
+ source: basename(path)
2345
+ }) : originalError, line = processedPos.line ?? originalError.line, column = processedPos.column ?? originalError.column, index = indexMap.get(`${line}:${column}`), definition = index != null && sortedDefinitions.find((def) => def.start <= index && def.end >= index), suite = definition ? definition.task : file, state = suite.mode === "run" || suite.mode === "only" ? "fail" : suite.mode, errors = suite.result?.errors || [];
2346
+ if (suite.result = {
2347
+ state,
2348
+ errors
2349
+ }, errors.push(error), state === "fail") {
2350
+ if (suite.suite) markState(suite.suite, "fail");
2351
+ else if (suite.file && suite !== suite.file) markState(suite.file, "fail");
2352
+ }
2353
+ }), this.markPassed(file);
2354
+ }), typeErrors.forEach((errors, path) => {
2355
+ if (!testFiles.has(path)) sourceErrors.push(...errors.map(({ error }) => error));
2356
+ }), {
2357
+ files,
2358
+ sourceErrors,
2359
+ time: performance$1.now() - this._startTime
2360
+ };
2361
+ }
2362
+ async parseTscLikeOutput(output) {
2363
+ const errorsMap = await getRawErrsMapFromTsCompile(output), typesErrors = /* @__PURE__ */ new Map();
2364
+ return errorsMap.forEach((errors, path) => {
2365
+ const filepath = resolve$1(this.project.config.root, path), suiteErrors = errors.map((info) => {
2366
+ const limit = Error.stackTraceLimit;
2367
+ Error.stackTraceLimit = 0;
2368
+ // Some expect-type errors have the most useful information on the second line e.g. `This expression is not callable.\n Type 'ExpectString<number>' has no call signatures.`
2369
+ const errMsg = info.errMsg.replace(/\r?\n\s*(Type .* has no call signatures)/g, " $1"), error = new TypeCheckError(errMsg, [{
2370
+ file: filepath,
2371
+ line: info.line,
2372
+ column: info.column,
2373
+ method: ""
2374
+ }]);
2375
+ return Error.stackTraceLimit = limit, {
2376
+ originalError: info,
2377
+ error: {
2378
+ name: error.name,
2379
+ message: errMsg,
2380
+ stacks: error.stacks,
2381
+ stack: ""
2382
+ }
2383
+ };
2384
+ });
2385
+ typesErrors.set(filepath, suiteErrors);
2386
+ }), typesErrors;
2387
+ }
2388
+ async stop() {
2389
+ this.process?.kill(), this.process = void 0;
2390
+ }
2391
+ async ensurePackageInstalled(ctx, checker) {
2392
+ if (checker !== "tsc" && checker !== "vue-tsc") return;
2393
+ const packageName = checker === "tsc" ? "typescript" : "vue-tsc";
2394
+ await ctx.packageInstaller.ensureInstalled(packageName, ctx.config.root);
2395
+ }
2396
+ getExitCode() {
2397
+ return this.process?.exitCode != null && this.process.exitCode;
2398
+ }
2399
+ getOutput() {
2400
+ return this._output;
2401
+ }
2402
+ async spawn() {
2403
+ const { root, watch, typecheck } = this.project.config, args = [
2404
+ "--noEmit",
2405
+ "--pretty",
2406
+ "false",
2407
+ "--incremental",
2408
+ "--tsBuildInfoFile",
2409
+ join(process.versions.pnp ? join(nodeos__default.tmpdir(), this.project.hash) : distDir, "tsconfig.tmp.tsbuildinfo")
2410
+ ];
2411
+ // use builtin watcher because it's faster
2412
+ if (watch) args.push("--watch");
2413
+ if (typecheck.allowJs) args.push("--allowJs", "--checkJs");
2414
+ if (typecheck.tsconfig) args.push("-p", resolve$1(root, typecheck.tsconfig));
2415
+ this._output = "", this._startTime = performance$1.now();
2416
+ const child = x(typecheck.checker, args, {
2417
+ nodeOptions: {
2418
+ cwd: root,
2419
+ stdio: "pipe"
2420
+ },
2421
+ throwOnError: false
2422
+ });
2423
+ this.process = child.process;
2424
+ let rerunTriggered = false, dataReceived = false;
2425
+ return new Promise((resolve, reject) => {
2426
+ if (!child.process || !child.process.stdout) {
2427
+ reject(/* @__PURE__ */ new Error(`Failed to initialize ${typecheck.checker}. This is a bug in Vitest - please, open an issue with reproduction.`));
2428
+ return;
2429
+ }
2430
+ child.process.stdout.on("data", (chunk) => {
2431
+ if (dataReceived = true, this._output += chunk, watch) {
2432
+ if (this._output.includes("File change detected") && !rerunTriggered) this._onWatcherRerun?.(), this._startTime = performance$1.now(), this._result.sourceErrors = [], this._result.files = [], this._tests = null, rerunTriggered = true;
2433
+ if (/Found \w+ errors*. Watching for/.test(this._output)) rerunTriggered = false, this.prepareResults(this._output).then((result) => {
2434
+ this._result = result, this._onParseEnd?.(result);
2435
+ }), this._output = "";
2436
+ }
2437
+ });
2438
+ const timeout = setTimeout(() => reject(/* @__PURE__ */ new Error(`${typecheck.checker} spawn timed out`)), this.project.config.typecheck.spawnTimeout);
2439
+ function onError(cause) {
2440
+ clearTimeout(timeout), reject(new Error("Spawning typechecker failed - is typescript installed?", { cause }));
2441
+ }
2442
+ if (child.process.once("spawn", () => {
2443
+ if (this._onParseStart?.(), child.process?.off("error", onError), clearTimeout(timeout), process.platform === "win32")
2444
+ // on Windows, the process might be spawned but fail to start
2445
+ // we wait for a potential error here. if "close" event didn't trigger,
2446
+ // we resolve the promise
2447
+ setTimeout(() => {
2448
+ resolve({ result: child });
2449
+ }, 200);
2450
+ else resolve({ result: child });
2451
+ }), process.platform === "win32") child.process.once("close", (code) => {
2452
+ if (code != null && code !== 0 && !dataReceived) onError(/* @__PURE__ */ new Error(`The ${typecheck.checker} command exited with code ${code}.`));
2453
+ });
2454
+ child.process.once("error", onError);
2455
+ });
2456
+ }
2457
+ async start() {
2458
+ if (this.process) return;
2459
+ const { watch } = this.project.config, { result: child } = await this.spawn();
2460
+ if (!watch) await child, this._result = await this.prepareResults(this._output), await this._onParseEnd?.(this._result);
2461
+ }
2462
+ getResult() {
2463
+ return this._result;
2464
+ }
2465
+ getTestFiles() {
2466
+ return Object.values(this._tests || {}).map((i) => i.file);
2467
+ }
2468
+ getTestPacksAndEvents() {
2469
+ const packs = [], events = [];
2470
+ for (const { file } of Object.values(this._tests || {})) {
2471
+ const result = convertTasksToEvents(file);
2472
+ packs.push(...result.packs), events.push(...result.events);
2473
+ }
2474
+ return {
2475
+ packs,
2476
+ events
2477
+ };
2478
+ }
2479
+ }
2480
+ function findGeneratedPosition(traceMap, { line, column, source }) {
2481
+ const found = generatedPositionFor(traceMap, {
2482
+ line,
2483
+ column,
2484
+ source
2485
+ });
2486
+ if (found.line !== null) return found;
2487
+ // find the next source token position when the exact error position doesn't exist in source map.
2488
+ // this can happen, for example, when the type error is in the comment "// @ts-expect-error"
2489
+ // and comments are stripped away in the generated code.
2490
+ const mappings = [];
2491
+ eachMapping(traceMap, (m) => {
2492
+ if (m.source === source && m.originalLine !== null && m.originalColumn !== null && (line === m.originalLine ? column < m.originalColumn : line < m.originalLine)) mappings.push(m);
2493
+ });
2494
+ const next = mappings.sort((a, b) => a.originalLine === b.originalLine ? a.originalColumn - b.originalColumn : a.originalLine - b.originalLine).at(0);
2495
+ return next ? {
2496
+ line: next.generatedLine,
2497
+ column: next.generatedColumn
2498
+ } : {
2499
+ line: null,
2500
+ column: null
2501
+ };
2502
+ }
2503
+
1100
2504
  // use Logger with custom Console to capture entire error printing
1101
2505
  function capturePrintError(error, ctx, options) {
1102
2506
  let output = "";
@@ -1105,12 +2509,12 @@ function capturePrintError(error, ctx, options) {
1105
2509
  } }), console = new Console(writable), logger = {
1106
2510
  error: console.error.bind(console),
1107
2511
  highlight: ctx.logger.highlight.bind(ctx.logger)
1108
- }, result = printError(error, ctx, logger, {
1109
- showCodeFrame: false,
1110
- ...options
1111
- });
2512
+ };
1112
2513
  return {
1113
- nearest: result?.nearest,
2514
+ nearest: printError(error, ctx, logger, {
2515
+ showCodeFrame: false,
2516
+ ...options
2517
+ })?.nearest,
1114
2518
  output
1115
2519
  };
1116
2520
  }
@@ -1158,19 +2562,17 @@ function printErrorInner(error, project, options) {
1158
2562
  const stacks = options.parseErrorStacktrace(e), nearest = error instanceof TypeCheckError ? error.stacks[0] : stacks.find((stack) => {
1159
2563
  // we are checking that this module was processed by us at one point
1160
2564
  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);
2565
+ return [...Object.values(project._vite?.environments || {}), ...Object.values(project.browser?.vite.environments || {})].some((environment) => {
2566
+ return [...environment.moduleGraph.getModulesByFile(stack.file)?.values() || []].some((module) => !!module.transformResult);
2567
+ }) && existsSync(stack.file);
1166
2568
  } catch {
1167
2569
  return false;
1168
2570
  }
1169
2571
  });
1170
2572
  if (type) printErrorType(type, project.vitest);
1171
2573
  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();
2574
+ const uniqueScreenshots = Array.from(new Set(options.screenshotPaths)), length = uniqueScreenshots.length;
2575
+ 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
2576
  }
1175
2577
  if (e.codeFrame) logger.error(`${e.codeFrame}\n`);
1176
2578
  if ("__vitest_rollup_error__" in e) {
@@ -1252,7 +2654,7 @@ function handleImportOutsideModuleError(stack, logger) {
1252
2654
  if (!esmErrors.some((e) => stack.includes(e))) return;
1253
2655
  const path = normalize(stack.split("\n")[0].trim());
1254
2656
  let name = path.split("/node_modules/").pop() || "";
1255
- if (name?.startsWith("@")) name = name.split("/").slice(0, 2).join("/");
2657
+ if (name[0] === "@") name = name.split("/").slice(0, 2).join("/");
1256
2658
  else name = name.split("/")[0];
1257
2659
  if (name) printModuleWarningForPackage(logger, path, name);
1258
2660
  else printModuleWarningForSourceCode(logger, path);
@@ -1348,7 +2750,7 @@ class GithubActionsReporter {
1348
2750
  this.ctx = ctx;
1349
2751
  }
1350
2752
  onTestCaseAnnotate(testCase, annotation) {
1351
- if (!annotation.location) return;
2753
+ if (!annotation.location || this.options.displayAnnotations === false) return;
1352
2754
  const type = getTitle(annotation.type), formatted = formatMessage({
1353
2755
  command: getType(annotation.type),
1354
2756
  properties: {
@@ -1409,7 +2811,7 @@ const BUILT_IN_TYPES = [
1409
2811
  "warning"
1410
2812
  ];
1411
2813
  function getTitle(type) {
1412
- return BUILT_IN_TYPES.includes(type) ? void 0 : type;
2814
+ if (!BUILT_IN_TYPES.includes(type)) return type;
1413
2815
  }
1414
2816
  function getType(type) {
1415
2817
  return BUILT_IN_TYPES.includes(type) ? type : "notice";
@@ -1436,8 +2838,7 @@ function escapeProperty(s) {
1436
2838
  class HangingProcessReporter {
1437
2839
  whyRunning;
1438
2840
  onInit() {
1439
- const _require = createRequire(import.meta.url);
1440
- this.whyRunning = _require("why-is-node-running");
2841
+ this.whyRunning = createRequire(import.meta.url)("why-is-node-running");
1441
2842
  }
1442
2843
  onProcessTimeout() {
1443
2844
  this.whyRunning?.();
@@ -1525,7 +2926,7 @@ class JsonReporter {
1525
2926
  async writeReport(report) {
1526
2927
  const outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "json");
1527
2928
  if (outputFile) {
1528
- const reportFile = resolve(this.ctx.config.root, outputFile), outputDirectory = dirname(reportFile);
2929
+ const reportFile = resolve$1(this.ctx.config.root, outputFile), outputDirectory = dirname(reportFile);
1529
2930
  if (!existsSync(outputDirectory)) await promises.mkdir(outputDirectory, { recursive: true });
1530
2931
  await promises.writeFile(reportFile, report, "utf-8"), this.ctx.logger.log(`JSON report written to ${reportFile}`);
1531
2932
  } else this.ctx.logger.log(report);
@@ -1594,11 +2995,10 @@ class JUnitReporter {
1594
2995
  this.ctx = ctx;
1595
2996
  const outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "junit");
1596
2997
  if (outputFile) {
1597
- this.reportFile = resolve(this.ctx.config.root, outputFile);
2998
+ this.reportFile = resolve$1(this.ctx.config.root, outputFile);
1598
2999
  const outputDirectory = dirname(this.reportFile);
1599
3000
  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) => {
3001
+ this.fileFd = await promises.open(this.reportFile, "w+"), this.baseLog = async (text) => {
1602
3002
  if (!this.fileFd) this.fileFd = await promises.open(this.reportFile, "w+");
1603
3003
  await promises.writeFile(this.fileFd, `${text}\n`);
1604
3004
  };
@@ -1761,7 +3161,7 @@ class TapReporter {
1761
3161
  this.logger.log(`# ${type}: ${message}`);
1762
3162
  }), this.logger.unindent();
1763
3163
  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];
3164
+ const stack = (task.file.pool === "browser" ? project.browser?.parseErrorStacktrace(error) || [] : parseErrorStacktrace(error, { frameFilter: this.ctx.config.onStackTrace }))[0];
1765
3165
  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
3166
  if (error.showDiff) this.logger.log(`actual: ${yamlString(error.actual)}`), this.logger.log(`expected: ${yamlString(error.expected)}`);
1767
3167
  }), this.logger.log("..."), this.logger.unindent();
@@ -1792,48 +3192,26 @@ class TapFlatReporter extends TapReporter {
1792
3192
  }
1793
3193
  }
1794
3194
 
3195
+ class TreeReporter extends DefaultReporter {
3196
+ verbose = true;
3197
+ renderSucceed = true;
3198
+ }
3199
+
1795
3200
  class VerboseReporter extends DefaultReporter {
1796
3201
  verbose = true;
1797
3202
  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);
3203
+ printTestModule(_module) {
3204
+ // don't print test module, only print tests
1804
3205
  }
1805
3206
  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;
3207
+ super.onTestCaseResult(test);
1809
3208
  const testResult = test.result();
1810
3209
  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}`)));
3210
+ let title = ` ${this.getEntityPrefix(test)} `;
3211
+ if (title += test.module.task.name, test.location) title += c.dim(`:${test.location.line}:${test.location.column}`);
3212
+ 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
3213
  if (test.annotations().length) this.log(), this.printAnnotations(test, "log", 3), this.log();
1819
3214
  }
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
3215
  }
1838
3216
 
1839
3217
  const ReportersMap = {
@@ -1845,8 +3223,9 @@ const ReportersMap = {
1845
3223
  "tap": TapReporter,
1846
3224
  "tap-flat": TapFlatReporter,
1847
3225
  "junit": JUnitReporter,
3226
+ "tree": TreeReporter,
1848
3227
  "hanging-process": HangingProcessReporter,
1849
3228
  "github-actions": GithubActionsReporter
1850
3229
  };
1851
3230
 
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 };
3231
+ 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, TraceMap as e, formatProjectName as f, getStateSymbol as g, ancestor as h, printError as i, errorBanner as j, divider as k, Typechecker as l, generateCodeFrame as m, createDefinesScript as n, originalPositionFor as o, parse$1 as p, convertTasksToEvents as q, readBlobs as r, separator as s, truncateString as t, utils as u, withLabel as w };