vitest 2.1.0-beta.5 → 2.1.0-beta.7

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 (62) hide show
  1. package/LICENSE.md +59 -320
  2. package/dist/browser.d.ts +6 -3
  3. package/dist/browser.js +2 -2
  4. package/dist/chunks/{RandomSequencer.B4M2ux5b.js → RandomSequencer.Bh5-tlNJ.js} +77 -58
  5. package/dist/chunks/{base.BH-FAiX7.js → base.BlXpj3e_.js} +1 -29
  6. package/dist/chunks/{base.BYvKfYzm.js → base.CchlWrnV.js} +2 -4
  7. package/dist/chunks/{cac.DosbLiJg.js → cac.B5XYKv2_.js} +26 -17
  8. package/dist/chunks/{cli-api.D8zgNrBU.js → cli-api.ByZPnilx.js} +7419 -7959
  9. package/dist/chunks/{constants.CaAN7icJ.js → constants.fzPh7AOq.js} +1 -1
  10. package/dist/chunks/{coverage.CqfT4xaf.js → coverage.zlNdAMHK.js} +5 -3
  11. package/dist/chunks/{creator.COdKw_ZN.js → creator.zfBZSJzo.js} +9 -9
  12. package/dist/chunks/{environment.0M5R1SX_.d.ts → environment.C5eAp3K6.d.ts} +1 -1
  13. package/dist/chunks/{execute.DT9BA6zp.js → execute._eQQfgI8.js} +322 -232
  14. package/dist/chunks/{git.ZtkbKc8u.js → git.B5SDxu-n.js} +5 -5
  15. package/dist/chunks/{globals.DRPLtPOv.js → globals.jM7MxN2t.js} +4 -4
  16. package/dist/chunks/{index.CM5UI-4O.js → index.Bn75ITYg.js} +3 -3
  17. package/dist/chunks/index.CPD77dLA.js +133 -0
  18. package/dist/chunks/{index.CxWPpGJz.js → index.CSjyR2-v.js} +2 -2
  19. package/dist/chunks/{index.Dx3f477d.js → index.DpJO1tkB.js} +1110 -1104
  20. package/dist/chunks/{index.CNZXZ9PJ.js → index.xm8OIiKD.js} +1 -1
  21. package/dist/chunks/mocker.cRtM890J.d.ts +17 -0
  22. package/dist/chunks/{reporters.DbwOGCsU.d.ts → reporters.WnPwkmgA.d.ts} +154 -129
  23. package/dist/chunks/{resolveConfig.RHsAM2_Q.js → resolveConfig.-K5hHm0S.js} +56 -34
  24. package/dist/chunks/{runBaseTests.BAhL8UH_.js → runBaseTests.Cztfoflv.js} +7 -7
  25. package/dist/chunks/{setup-common.KBrCO5LJ.js → setup-common.fGBFoQKJ.js} +1 -1
  26. package/dist/chunks/{utils.C3_cBsyn.js → utils.Cn0zI1t3.js} +16 -3
  27. package/dist/chunks/{utils.DO38lwfj.js → utils.Dbnmsfq1.js} +1 -1
  28. package/dist/chunks/{vi.B6QZ938s.js → vi.DGgiNzJE.js} +46 -37
  29. package/dist/chunks/vite.D2yAwzwa.d.ts +11 -0
  30. package/dist/chunks/{vm.kl9T_5ai.js → vm.CPXwWp4C.js} +1 -2
  31. package/dist/chunks/{worker.Cx2xE71X.d.ts → worker.Bws9Zuxu.d.ts} +1 -1
  32. package/dist/chunks/{worker.BANO5ak1.d.ts → worker.CmPmTxgH.d.ts} +2 -14
  33. package/dist/cli.js +3 -3
  34. package/dist/config.cjs +1 -0
  35. package/dist/config.d.ts +70 -2
  36. package/dist/config.js +1 -0
  37. package/dist/coverage.d.ts +4 -3
  38. package/dist/coverage.js +29 -5
  39. package/dist/environments.d.ts +2 -2
  40. package/dist/execute.d.ts +13 -14
  41. package/dist/execute.js +3 -2
  42. package/dist/index.d.ts +23 -16
  43. package/dist/index.js +4 -4
  44. package/dist/mocker.d.ts +1 -0
  45. package/dist/mocker.js +1 -0
  46. package/dist/node.d.ts +27 -9
  47. package/dist/node.js +44 -39
  48. package/dist/reporters.d.ts +2 -2
  49. package/dist/reporters.js +8 -8
  50. package/dist/runners.js +2 -2
  51. package/dist/utils.d.ts +1 -0
  52. package/dist/worker.js +3 -2
  53. package/dist/workers/forks.js +5 -4
  54. package/dist/workers/runVmTests.js +6 -6
  55. package/dist/workers/threads.js +5 -4
  56. package/dist/workers/vmForks.js +4 -4
  57. package/dist/workers/vmThreads.js +4 -4
  58. package/dist/workers.d.ts +4 -16
  59. package/dist/workers.js +6 -5
  60. package/mocker.d.ts +1 -0
  61. package/package.json +22 -17
  62. package/dist/chunks/index.DNUmWFkO.js +0 -319
@@ -1,287 +1,667 @@
1
- import { g as getTestName, h as hasFailedSnapshot, a as getFullName } from './tasks.BZnCS9aT.js';
2
1
  import fs, { existsSync, promises, readFileSync } from 'node:fs';
3
2
  import c from 'tinyrainbow';
4
3
  import * as pathe from 'pathe';
5
4
  import { relative, resolve, dirname, extname, normalize } from 'pathe';
5
+ import { g as getTestName, h as hasFailedSnapshot, a as getFullName } from './tasks.BZnCS9aT.js';
6
6
  import { notNullish, highlight, inspect, positionToOffset, lineSplitRE } from '@vitest/utils';
7
7
  import { a as isNode, b as isDeno } from './env.CmHVDJnw.js';
8
- import { g as getStateSymbol, f as formatProjectName, t as taskFail, F as F_RIGHT, a as F_POINTER, r as renderSnapshotSummary, b as getStateString, c as formatTimeString, d as countTestErrors, e as divider, s as stripAnsi, h as getCols, i as getHookStateSymbol } from './utils.DO38lwfj.js';
8
+ import { g as getStateSymbol, f as formatProjectName, t as taskFail, F as F_RIGHT, a as F_POINTER, r as renderSnapshotSummary, b as getStateString, c as formatTimeString, d as countTestErrors, e as divider, s as stripAnsi, h as getCols, i as getHookStateSymbol } from './utils.Dbnmsfq1.js';
9
9
  import { getTests, hasFailed, getSuites, getTasks } from '@vitest/runner/utils';
10
10
  import { performance } from 'node:perf_hooks';
11
11
  import { parseStacktrace, parseErrorStacktrace } from '@vitest/utils/source-map';
12
12
  import { r as relativePath } from './index.CxRxs566.js';
13
- import { t as toArray, c as isPrimitive } from './base.BH-FAiX7.js';
13
+ import { t as toArray, b as isPrimitive } from './base.BlXpj3e_.js';
14
14
  import { isCI } from 'std-env';
15
- import { g as getOutputFile, R as RandomSequencer, a as TypeCheckError } from './RandomSequencer.B4M2ux5b.js';
15
+ import { g as getOutputFile, R as RandomSequencer, a as TypeCheckError } from './RandomSequencer.Bh5-tlNJ.js';
16
16
  import { hostname } from 'node:os';
17
17
  import { Writable } from 'node:stream';
18
18
  import { Console } from 'node:console';
19
19
  import process$2 from 'node:process';
20
20
  import { g as getDefaultExportFromCjs, c as commonjsGlobal } from './_commonjsHelpers.BFTU3MAI.js';
21
- import require$$0$1 from 'assert';
22
- import require$$0 from 'events';
21
+ import require$$0 from 'assert';
22
+ import require$$2 from 'events';
23
23
  import { createRequire } from 'node:module';
24
24
  import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
25
- import { cleanUrl } from 'vite-node/utils';
26
-
27
- /// <reference types="../types/index.d.ts" />
28
-
29
- // (c) 2020-present Andrea Giammarchi
30
-
31
- const {parse: $parse, stringify: $stringify} = JSON;
32
- const {keys} = Object;
33
-
34
- const Primitive = String; // it could be Number
35
- const primitive = 'string'; // it could be 'number'
36
-
37
- const ignore = {};
38
- const object = 'object';
39
-
40
- const noop = (_, value) => value;
41
-
42
- const primitives = value => (
43
- value instanceof Primitive ? Primitive(value) : value
44
- );
45
-
46
- const Primitives = (_, value) => (
47
- typeof value === primitive ? new Primitive(value) : value
48
- );
49
25
 
50
- const revive = (input, parsed, output, $) => {
51
- const lazy = [];
52
- for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
53
- const k = ke[y];
54
- const value = output[k];
55
- if (value instanceof Primitive) {
56
- const tmp = input[value];
57
- if (typeof tmp === object && !parsed.has(tmp)) {
58
- parsed.add(tmp);
59
- output[k] = ignore;
60
- lazy.push({k, a: [input, parsed, tmp, $]});
61
- }
62
- else
63
- output[k] = $.call(output, k, tmp);
64
- }
65
- else if (output[k] !== ignore)
66
- output[k] = $.call(output, k, value);
26
+ class TestProject {
27
+ /**
28
+ * The global vitest instance.
29
+ * @experimental The public Vitest API is experimental and does not follow semver.
30
+ */
31
+ vitest;
32
+ /**
33
+ * The workspace project this test project is associated with.
34
+ * @experimental The public Vitest API is experimental and does not follow semver.
35
+ */
36
+ workspaceProject;
37
+ /**
38
+ * Resolved project configuration.
39
+ */
40
+ config;
41
+ /**
42
+ * Resolved global configuration. If there are no workspace projects, this will be the same as `config`.
43
+ */
44
+ globalConfig;
45
+ /**
46
+ * The name of the project or an empty string if not set.
47
+ */
48
+ name;
49
+ constructor(workspaceProject) {
50
+ this.workspaceProject = workspaceProject;
51
+ this.vitest = workspaceProject.ctx;
52
+ this.globalConfig = workspaceProject.ctx.config;
53
+ this.config = workspaceProject.config;
54
+ this.name = workspaceProject.getName();
67
55
  }
68
- for (let {length} = lazy, i = 0; i < length; i++) {
69
- const {k, a} = lazy[i];
70
- output[k] = $.call(output, k, revive.apply(null, a));
56
+ /**
57
+ * Serialized project configuration. This is the config that tests receive.
58
+ */
59
+ get serializedConfig() {
60
+ return this.workspaceProject.getSerializableConfig();
71
61
  }
72
- return output;
73
- };
74
-
75
- const set = (known, input, value) => {
76
- const index = Primitive(input.push(value) - 1);
77
- known.set(value, index);
78
- return index;
79
- };
80
-
81
- /**
82
- * Converts a specialized flatted string into a JS value.
83
- * @param {string} text
84
- * @param {(this: any, key: string, value: any) => any} [reviver]
85
- * @returns {any}
86
- */
87
- const parse = (text, reviver) => {
88
- const input = $parse(text, Primitives).map(primitives);
89
- const value = input[0];
90
- const $ = reviver || noop;
91
- const tmp = typeof value === object && value ?
92
- revive(input, new Set, value, $) :
93
- value;
94
- return $.call({'': tmp}, '', tmp);
95
- };
96
-
97
- /**
98
- * Converts a JS value into a specialized flatted string.
99
- * @param {any} value
100
- * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
101
- * @param {string | number | undefined} [space]
102
- * @returns {string}
103
- */
104
- const stringify = (value, replacer, space) => {
105
- const $ = replacer && typeof replacer === object ?
106
- (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
107
- (replacer || noop);
108
- const known = new Map;
109
- const input = [];
110
- const output = [];
111
- let i = +set(known, input, $.call({'': value}, '', value));
112
- let firstRun = !i;
113
- while (i < input.length) {
114
- firstRun = true;
115
- output[i] = $stringify(input[i++], replace, space);
62
+ /**
63
+ * Custom context provided to the project.
64
+ */
65
+ context() {
66
+ return this.workspaceProject.getProvidedContext();
116
67
  }
117
- return '[' + output.join(',') + ']';
118
- function replace(key, value) {
119
- if (firstRun) {
120
- firstRun = !firstRun;
121
- return value;
122
- }
123
- const after = $.call(this, key, value);
124
- switch (typeof after) {
125
- case object:
126
- if (after === null) return after;
127
- case primitive:
128
- return known.get(after) || set(known, input, after);
129
- }
130
- return after;
68
+ /**
69
+ * Provide a custom serializable context to the project. This context will be available for tests once they run.
70
+ */
71
+ provide(key, value) {
72
+ this.workspaceProject.provide(key, value);
131
73
  }
132
- };
74
+ toJSON() {
75
+ return {
76
+ name: this.name,
77
+ serializedConfig: this.serializedConfig,
78
+ context: this.context()
79
+ };
80
+ }
81
+ }
133
82
 
134
- const BADGE_PADDING = " ";
135
- const HELP_HINT = `${c.dim("press ")}${c.bold("h")}${c.dim(" to show help")}`;
136
- const HELP_UPDATE_SNAP = c.dim("press ") + c.bold(c.yellow("u")) + c.dim(" to update snapshot");
137
- const HELP_QUITE = `${c.dim("press ")}${c.bold("q")}${c.dim(" to quit")}`;
138
- const WAIT_FOR_CHANGE_PASS = `
139
- ${c.bold(
140
- c.inverse(c.green(" PASS "))
141
- )}${c.green(" Waiting for file changes...")}`;
142
- const WAIT_FOR_CHANGE_FAIL = `
143
- ${c.bold(c.inverse(c.red(" FAIL ")))}${c.red(
144
- " Tests failed. Watching for file changes..."
145
- )}`;
146
- const WAIT_FOR_CHANGE_CANCELLED = `
147
- ${c.bold(
148
- c.inverse(c.red(" CANCELLED "))
149
- )}${c.red(" Test run cancelled. Watching for file changes...")}`;
150
- const LAST_RUN_LOG_TIMEOUT = 1500;
151
- class BaseReporter {
152
- start = 0;
153
- end = 0;
154
- watchFilters;
155
- failedUnwatchedFiles = [];
156
- isTTY;
157
- ctx = void 0;
158
- verbose = false;
159
- _filesInWatchMode = /* @__PURE__ */ new Map();
160
- _lastRunTimeout = 0;
161
- _lastRunTimer;
162
- _lastRunCount = 0;
163
- _timeStart = /* @__PURE__ */ new Date();
164
- _offUnhandledRejection;
165
- constructor(options = {}) {
166
- this.isTTY = options.isTTY ?? ((isNode || isDeno) && process.stdout?.isTTY && !isCI);
167
- this.registerUnhandledRejection();
83
+ class ReportedTaskImplementation {
84
+ /**
85
+ * Task instance.
86
+ * @experimental Public runner task API is experimental and does not follow semver.
87
+ */
88
+ task;
89
+ /**
90
+ * The project assosiacted with the test or suite.
91
+ */
92
+ project;
93
+ /**
94
+ * Unique identifier.
95
+ * This ID is deterministic and will be the same for the same test across multiple runs.
96
+ * The ID is based on the project name, module url and test position.
97
+ */
98
+ id;
99
+ /**
100
+ * Location in the module where the test or suite is defined.
101
+ */
102
+ location;
103
+ constructor(task, project) {
104
+ this.task = task;
105
+ this.project = project.testProject || (project.testProject = new TestProject(project));
106
+ this.id = task.id;
107
+ this.location = task.location;
168
108
  }
169
- get mode() {
170
- return this.ctx.config.mode;
109
+ /**
110
+ * Creates a new reported task instance and stores it in the project's state for future use.
111
+ */
112
+ static register(task, project) {
113
+ const state = new this(task, project);
114
+ storeTask(project, task, state);
115
+ return state;
171
116
  }
172
- onInit(ctx) {
173
- this.ctx = ctx;
174
- ctx.onClose(() => {
175
- this._offUnhandledRejection?.();
176
- });
177
- ctx.logger.printBanner();
178
- this.start = performance.now();
117
+ }
118
+ class TestCase extends ReportedTaskImplementation {
119
+ #fullName;
120
+ type = "test";
121
+ /**
122
+ * Direct reference to the test module where the test or suite is defined.
123
+ */
124
+ module;
125
+ /**
126
+ * Name of the test.
127
+ */
128
+ name;
129
+ /**
130
+ * Options that the test was initiated with.
131
+ */
132
+ options;
133
+ /**
134
+ * Parent suite. If the test was called directly inside the module, the parent will be the module itself.
135
+ */
136
+ parent;
137
+ constructor(task, project) {
138
+ super(task, project);
139
+ this.name = task.name;
140
+ this.module = getReportedTask(project, task.file);
141
+ const suite = this.task.suite;
142
+ if (suite) {
143
+ this.parent = getReportedTask(project, suite);
144
+ } else {
145
+ this.parent = this.module;
146
+ }
147
+ this.options = buildOptions(task);
179
148
  }
180
- relative(path) {
181
- return relativePath(this.ctx.config.root, path);
182
- }
183
- onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
184
- this.end = performance.now();
185
- this.reportSummary(files, errors);
186
- if (errors.length) {
187
- if (!this.ctx.config.dangerouslyIgnoreUnhandledErrors) {
188
- process.exitCode = 1;
189
- }
149
+ /**
150
+ * Full name of the test including all parent suites separated with `>`.
151
+ */
152
+ get fullName() {
153
+ if (this.#fullName === void 0) {
154
+ this.#fullName = getTestName(this.task, " > ");
190
155
  }
156
+ return this.#fullName;
191
157
  }
192
- onTaskUpdate(packs) {
193
- if (this.isTTY) {
194
- return;
195
- }
196
- for (const pack of packs) {
197
- const task = this.ctx.state.idMap.get(pack[0]);
198
- if (task) {
199
- this.printTask(task);
200
- }
158
+ /**
159
+ * Test results. Will be `undefined` if test is not finished yet or was just collected.
160
+ */
161
+ result() {
162
+ const result = this.task.result;
163
+ if (!result || result.state === "run") {
164
+ return void 0;
201
165
  }
166
+ const state = result.state === "fail" ? "failed" : result.state === "pass" ? "passed" : "skipped";
167
+ return {
168
+ state,
169
+ errors: result.errors
170
+ };
202
171
  }
203
- printTask(task) {
204
- if (!("filepath" in task) || !task.result?.state || task.result?.state === "run") {
205
- return;
206
- }
207
- const logger = this.ctx.logger;
208
- const tests = getTests(task);
209
- const failed = tests.filter((t) => t.result?.state === "fail");
210
- const skipped = tests.filter(
211
- (t) => t.mode === "skip" || t.mode === "todo"
212
- );
213
- let state = c.dim(`${tests.length} test${tests.length > 1 ? "s" : ""}`);
214
- if (failed.length) {
215
- state += ` ${c.dim("|")} ${c.red(`${failed.length} failed`)}`;
172
+ /**
173
+ * Checks if the test did not fail the suite.
174
+ * If the test is not finished yet or was skipped, it will return `true`.
175
+ */
176
+ ok() {
177
+ const result = this.result();
178
+ return !result || result.state !== "failed";
179
+ }
180
+ /**
181
+ * Custom metadata that was attached to the test during its execution.
182
+ */
183
+ meta() {
184
+ return this.task.meta;
185
+ }
186
+ /**
187
+ * Useful information about the test like duration, memory usage, etc.
188
+ * Diagnostic is only available after the test has finished.
189
+ */
190
+ diagnostic() {
191
+ const result = this.task.result;
192
+ if (!result || result.state === "run" || !result.startTime) {
193
+ return void 0;
216
194
  }
217
- if (skipped.length) {
218
- state += ` ${c.dim("|")} ${c.yellow(`${skipped.length} skipped`)}`;
195
+ return {
196
+ heap: result.heap,
197
+ duration: result.duration,
198
+ startTime: result.startTime,
199
+ retryCount: result.retryCount ?? 0,
200
+ repeatCount: result.repeatCount ?? 0,
201
+ flaky: !!result.retryCount && result.state === "pass" && result.retryCount > 0
202
+ };
203
+ }
204
+ }
205
+ class TestCollection {
206
+ #task;
207
+ #project;
208
+ constructor(task, project) {
209
+ this.#task = task;
210
+ this.#project = project;
211
+ }
212
+ /**
213
+ * Returns the test or suite at a specific index in the array.
214
+ */
215
+ at(index) {
216
+ if (index < 0) {
217
+ index = this.size + index;
219
218
  }
220
- let suffix = c.dim(" (") + state + c.dim(")");
221
- if (task.result.duration) {
222
- const color = task.result.duration > this.ctx.config.slowTestThreshold ? c.yellow : c.gray;
223
- suffix += color(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
219
+ return getReportedTask(this.#project, this.#task.tasks[index]);
220
+ }
221
+ /**
222
+ * The number of tests and suites in the collection.
223
+ */
224
+ get size() {
225
+ return this.#task.tasks.length;
226
+ }
227
+ /**
228
+ * Returns the collection in array form for easier manipulation.
229
+ */
230
+ array() {
231
+ return Array.from(this);
232
+ }
233
+ /**
234
+ * Filters all tests that are part of this collection and its children.
235
+ */
236
+ *allTests(state) {
237
+ for (const child of this) {
238
+ if (child.type === "suite") {
239
+ yield* child.children.allTests(state);
240
+ } else if (state) {
241
+ const testState = getTestState(child);
242
+ if (state === testState) {
243
+ yield child;
244
+ }
245
+ } else {
246
+ yield child;
247
+ }
224
248
  }
225
- if (this.ctx.config.logHeapUsage && task.result.heap != null) {
226
- suffix += c.magenta(
227
- ` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`
228
- );
249
+ }
250
+ /**
251
+ * Filters only the tests that are part of this collection.
252
+ */
253
+ *tests(state) {
254
+ for (const child of this) {
255
+ if (child.type !== "test") {
256
+ continue;
257
+ }
258
+ if (state) {
259
+ const testState = getTestState(child);
260
+ if (state === testState) {
261
+ yield child;
262
+ }
263
+ } else {
264
+ yield child;
265
+ }
229
266
  }
230
- let title = ` ${getStateSymbol(task)} `;
231
- if (task.projectName) {
232
- title += formatProjectName(task.projectName);
267
+ }
268
+ /**
269
+ * Filters only the suites that are part of this collection.
270
+ */
271
+ *suites() {
272
+ for (const child of this) {
273
+ if (child.type === "suite") {
274
+ yield child;
275
+ }
233
276
  }
234
- title += `${task.name} ${suffix}`;
235
- logger.log(title);
236
- for (const test of failed) {
237
- logger.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}`));
238
- test.result?.errors?.forEach((e) => {
239
- logger.log(c.red(` ${F_RIGHT} ${e?.message}`));
240
- });
277
+ }
278
+ /**
279
+ * Filters all suites that are part of this collection and its children.
280
+ */
281
+ *allSuites() {
282
+ for (const child of this) {
283
+ if (child.type === "suite") {
284
+ yield child;
285
+ yield* child.children.allSuites();
286
+ }
241
287
  }
242
288
  }
243
- onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
244
- this.resetLastRunLog();
245
- const failed = errors.length > 0 || hasFailed(files);
246
- const failedSnap = hasFailedSnapshot(files);
247
- const cancelled = this.ctx.isCancelling;
248
- if (failed) {
249
- this.ctx.logger.log(WAIT_FOR_CHANGE_FAIL);
250
- } else if (cancelled) {
251
- this.ctx.logger.log(WAIT_FOR_CHANGE_CANCELLED);
252
- } else {
253
- this.ctx.logger.log(WAIT_FOR_CHANGE_PASS);
289
+ *[Symbol.iterator]() {
290
+ for (const task of this.#task.tasks) {
291
+ yield getReportedTask(this.#project, task);
254
292
  }
255
- const hints = [];
256
- hints.push(HELP_HINT);
257
- if (failedSnap) {
258
- hints.unshift(HELP_UPDATE_SNAP);
293
+ }
294
+ }
295
+ class SuiteImplementation extends ReportedTaskImplementation {
296
+ /**
297
+ * Collection of suites and tests that are part of this suite.
298
+ */
299
+ children;
300
+ constructor(task, project) {
301
+ super(task, project);
302
+ this.children = new TestCollection(task, project);
303
+ }
304
+ }
305
+ class TestSuite extends SuiteImplementation {
306
+ #fullName;
307
+ type = "suite";
308
+ /**
309
+ * Name of the test or the suite.
310
+ */
311
+ name;
312
+ /**
313
+ * Direct reference to the test module where the test or suite is defined.
314
+ */
315
+ module;
316
+ /**
317
+ * Parent suite. If suite was called directly inside the module, the parent will be the module itself.
318
+ */
319
+ parent;
320
+ /**
321
+ * Options that suite was initiated with.
322
+ */
323
+ options;
324
+ constructor(task, project) {
325
+ super(task, project);
326
+ this.name = task.name;
327
+ this.module = getReportedTask(project, task.file);
328
+ const suite = this.task.suite;
329
+ if (suite) {
330
+ this.parent = getReportedTask(project, suite);
259
331
  } else {
260
- hints.push(HELP_QUITE);
261
- }
262
- this.ctx.logger.log(BADGE_PADDING + hints.join(c.dim(", ")));
263
- if (this._lastRunCount) {
264
- const LAST_RUN_TEXT = `rerun x${this._lastRunCount}`;
265
- const LAST_RUN_TEXTS = [
266
- c.blue(LAST_RUN_TEXT),
267
- c.gray(LAST_RUN_TEXT),
268
- c.dim(c.gray(LAST_RUN_TEXT))
269
- ];
270
- this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[0]);
271
- this._lastRunTimeout = 0;
272
- this._lastRunTimer = setInterval(() => {
273
- this._lastRunTimeout += 1;
274
- if (this._lastRunTimeout >= LAST_RUN_TEXTS.length) {
275
- this.resetLastRunLog();
276
- } else {
277
- this.ctx.logger.logUpdate(
278
- BADGE_PADDING + LAST_RUN_TEXTS[this._lastRunTimeout]
279
- );
280
- }
281
- }, LAST_RUN_LOG_TIMEOUT / LAST_RUN_TEXTS.length);
332
+ this.parent = this.module;
282
333
  }
334
+ this.options = buildOptions(task);
283
335
  }
284
- resetLastRunLog() {
336
+ /**
337
+ * Full name of the suite including all parent suites separated with `>`.
338
+ */
339
+ get fullName() {
340
+ if (this.#fullName === void 0) {
341
+ this.#fullName = getTestName(this.task, " > ");
342
+ }
343
+ return this.#fullName;
344
+ }
345
+ }
346
+ class TestModule extends SuiteImplementation {
347
+ type = "module";
348
+ /**
349
+ * This is usually an absolute UNIX file path.
350
+ * It can be a virtual id if the file is not on the disk.
351
+ * This value corresponds to Vite's `ModuleGraph` id.
352
+ */
353
+ moduleId;
354
+ constructor(task, project) {
355
+ super(task, project);
356
+ this.moduleId = task.filepath;
357
+ }
358
+ /**
359
+ * Useful information about the module like duration, memory usage, etc.
360
+ * If the module was not executed yet, all diagnostic values will return `0`.
361
+ */
362
+ diagnostic() {
363
+ const setupDuration = this.task.setupDuration || 0;
364
+ const collectDuration = this.task.collectDuration || 0;
365
+ const prepareDuration = this.task.prepareDuration || 0;
366
+ const environmentSetupDuration = this.task.environmentLoad || 0;
367
+ const duration = this.task.result?.duration || 0;
368
+ return {
369
+ environmentSetupDuration,
370
+ prepareDuration,
371
+ collectDuration,
372
+ setupDuration,
373
+ duration
374
+ };
375
+ }
376
+ }
377
+ function buildOptions(task) {
378
+ return {
379
+ each: task.each,
380
+ concurrent: task.concurrent,
381
+ shuffle: task.shuffle,
382
+ retry: task.retry,
383
+ repeats: task.repeats,
384
+ mode: task.mode
385
+ };
386
+ }
387
+ function getTestState(test) {
388
+ const result = test.result();
389
+ return result ? result.state : "running";
390
+ }
391
+ function storeTask(project, runnerTask, reportedTask) {
392
+ project.ctx.state.reportedTasksMap.set(runnerTask, reportedTask);
393
+ }
394
+ function getReportedTask(project, runnerTask) {
395
+ const reportedTask = project.ctx.state.getReportedEntity(runnerTask);
396
+ if (!reportedTask) {
397
+ throw new Error(
398
+ `Task instance was not found for ${runnerTask.type} "${runnerTask.name}"`
399
+ );
400
+ }
401
+ return reportedTask;
402
+ }
403
+
404
+ /// <reference types="../types/index.d.ts" />
405
+
406
+ // (c) 2020-present Andrea Giammarchi
407
+
408
+ const {parse: $parse, stringify: $stringify} = JSON;
409
+ const {keys} = Object;
410
+
411
+ const Primitive = String; // it could be Number
412
+ const primitive = 'string'; // it could be 'number'
413
+
414
+ const ignore = {};
415
+ const object = 'object';
416
+
417
+ const noop = (_, value) => value;
418
+
419
+ const primitives = value => (
420
+ value instanceof Primitive ? Primitive(value) : value
421
+ );
422
+
423
+ const Primitives = (_, value) => (
424
+ typeof value === primitive ? new Primitive(value) : value
425
+ );
426
+
427
+ const revive = (input, parsed, output, $) => {
428
+ const lazy = [];
429
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
430
+ const k = ke[y];
431
+ const value = output[k];
432
+ if (value instanceof Primitive) {
433
+ const tmp = input[value];
434
+ if (typeof tmp === object && !parsed.has(tmp)) {
435
+ parsed.add(tmp);
436
+ output[k] = ignore;
437
+ lazy.push({k, a: [input, parsed, tmp, $]});
438
+ }
439
+ else
440
+ output[k] = $.call(output, k, tmp);
441
+ }
442
+ else if (output[k] !== ignore)
443
+ output[k] = $.call(output, k, value);
444
+ }
445
+ for (let {length} = lazy, i = 0; i < length; i++) {
446
+ const {k, a} = lazy[i];
447
+ output[k] = $.call(output, k, revive.apply(null, a));
448
+ }
449
+ return output;
450
+ };
451
+
452
+ const set = (known, input, value) => {
453
+ const index = Primitive(input.push(value) - 1);
454
+ known.set(value, index);
455
+ return index;
456
+ };
457
+
458
+ /**
459
+ * Converts a specialized flatted string into a JS value.
460
+ * @param {string} text
461
+ * @param {(this: any, key: string, value: any) => any} [reviver]
462
+ * @returns {any}
463
+ */
464
+ const parse = (text, reviver) => {
465
+ const input = $parse(text, Primitives).map(primitives);
466
+ const value = input[0];
467
+ const $ = reviver || noop;
468
+ const tmp = typeof value === object && value ?
469
+ revive(input, new Set, value, $) :
470
+ value;
471
+ return $.call({'': tmp}, '', tmp);
472
+ };
473
+
474
+ /**
475
+ * Converts a JS value into a specialized flatted string.
476
+ * @param {any} value
477
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
478
+ * @param {string | number | undefined} [space]
479
+ * @returns {string}
480
+ */
481
+ const stringify = (value, replacer, space) => {
482
+ const $ = replacer && typeof replacer === object ?
483
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
484
+ (replacer || noop);
485
+ const known = new Map;
486
+ const input = [];
487
+ const output = [];
488
+ let i = +set(known, input, $.call({'': value}, '', value));
489
+ let firstRun = !i;
490
+ while (i < input.length) {
491
+ firstRun = true;
492
+ output[i] = $stringify(input[i++], replace, space);
493
+ }
494
+ return '[' + output.join(',') + ']';
495
+ function replace(key, value) {
496
+ if (firstRun) {
497
+ firstRun = !firstRun;
498
+ return value;
499
+ }
500
+ const after = $.call(this, key, value);
501
+ switch (typeof after) {
502
+ case object:
503
+ if (after === null) return after;
504
+ case primitive:
505
+ return known.get(after) || set(known, input, after);
506
+ }
507
+ return after;
508
+ }
509
+ };
510
+
511
+ const BADGE_PADDING = " ";
512
+ const HELP_HINT = `${c.dim("press ")}${c.bold("h")}${c.dim(" to show help")}`;
513
+ const HELP_UPDATE_SNAP = c.dim("press ") + c.bold(c.yellow("u")) + c.dim(" to update snapshot");
514
+ const HELP_QUITE = `${c.dim("press ")}${c.bold("q")}${c.dim(" to quit")}`;
515
+ const WAIT_FOR_CHANGE_PASS = `
516
+ ${c.bold(
517
+ c.inverse(c.green(" PASS "))
518
+ )}${c.green(" Waiting for file changes...")}`;
519
+ const WAIT_FOR_CHANGE_FAIL = `
520
+ ${c.bold(c.inverse(c.red(" FAIL ")))}${c.red(
521
+ " Tests failed. Watching for file changes..."
522
+ )}`;
523
+ const WAIT_FOR_CHANGE_CANCELLED = `
524
+ ${c.bold(
525
+ c.inverse(c.red(" CANCELLED "))
526
+ )}${c.red(" Test run cancelled. Watching for file changes...")}`;
527
+ const LAST_RUN_LOG_TIMEOUT = 1500;
528
+ class BaseReporter {
529
+ start = 0;
530
+ end = 0;
531
+ watchFilters;
532
+ failedUnwatchedFiles = [];
533
+ isTTY;
534
+ ctx = void 0;
535
+ verbose = false;
536
+ _filesInWatchMode = /* @__PURE__ */ new Map();
537
+ _lastRunTimeout = 0;
538
+ _lastRunTimer;
539
+ _lastRunCount = 0;
540
+ _timeStart = /* @__PURE__ */ new Date();
541
+ _offUnhandledRejection;
542
+ constructor(options = {}) {
543
+ this.isTTY = options.isTTY ?? ((isNode || isDeno) && process.stdout?.isTTY && !isCI);
544
+ this.registerUnhandledRejection();
545
+ }
546
+ get mode() {
547
+ return this.ctx.config.mode;
548
+ }
549
+ onInit(ctx) {
550
+ this.ctx = ctx;
551
+ ctx.onClose(() => {
552
+ this._offUnhandledRejection?.();
553
+ });
554
+ ctx.logger.printBanner();
555
+ this.start = performance.now();
556
+ }
557
+ relative(path) {
558
+ return relativePath(this.ctx.config.root, path);
559
+ }
560
+ onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
561
+ this.end = performance.now();
562
+ this.reportSummary(files, errors);
563
+ if (errors.length) {
564
+ if (!this.ctx.config.dangerouslyIgnoreUnhandledErrors) {
565
+ process.exitCode = 1;
566
+ }
567
+ }
568
+ }
569
+ onTaskUpdate(packs) {
570
+ if (this.isTTY) {
571
+ return;
572
+ }
573
+ for (const pack of packs) {
574
+ const task = this.ctx.state.idMap.get(pack[0]);
575
+ if (task) {
576
+ this.printTask(task);
577
+ }
578
+ }
579
+ }
580
+ printTask(task) {
581
+ if (!("filepath" in task) || !task.result?.state || task.result?.state === "run") {
582
+ return;
583
+ }
584
+ const logger = this.ctx.logger;
585
+ const tests = getTests(task);
586
+ const failed = tests.filter((t) => t.result?.state === "fail");
587
+ const skipped = tests.filter(
588
+ (t) => t.mode === "skip" || t.mode === "todo"
589
+ );
590
+ let state = c.dim(`${tests.length} test${tests.length > 1 ? "s" : ""}`);
591
+ if (failed.length) {
592
+ state += ` ${c.dim("|")} ${c.red(`${failed.length} failed`)}`;
593
+ }
594
+ if (skipped.length) {
595
+ state += ` ${c.dim("|")} ${c.yellow(`${skipped.length} skipped`)}`;
596
+ }
597
+ let suffix = c.dim(" (") + state + c.dim(")");
598
+ if (task.result.duration) {
599
+ const color = task.result.duration > this.ctx.config.slowTestThreshold ? c.yellow : c.gray;
600
+ suffix += color(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
601
+ }
602
+ if (this.ctx.config.logHeapUsage && task.result.heap != null) {
603
+ suffix += c.magenta(
604
+ ` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`
605
+ );
606
+ }
607
+ let title = ` ${getStateSymbol(task)} `;
608
+ if (task.meta.typecheck) {
609
+ title += `${c.bgBlue(c.bold(" TS "))} `;
610
+ }
611
+ if (task.projectName) {
612
+ title += formatProjectName(task.projectName);
613
+ }
614
+ title += `${task.name} ${suffix}`;
615
+ logger.log(title);
616
+ for (const test of failed) {
617
+ logger.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}`));
618
+ test.result?.errors?.forEach((e) => {
619
+ logger.log(c.red(` ${F_RIGHT} ${e?.message}`));
620
+ });
621
+ }
622
+ }
623
+ onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
624
+ this.resetLastRunLog();
625
+ const failed = errors.length > 0 || hasFailed(files);
626
+ const failedSnap = hasFailedSnapshot(files);
627
+ const cancelled = this.ctx.isCancelling;
628
+ if (failed) {
629
+ this.ctx.logger.log(WAIT_FOR_CHANGE_FAIL);
630
+ } else if (cancelled) {
631
+ this.ctx.logger.log(WAIT_FOR_CHANGE_CANCELLED);
632
+ } else {
633
+ this.ctx.logger.log(WAIT_FOR_CHANGE_PASS);
634
+ }
635
+ const hints = [];
636
+ hints.push(HELP_HINT);
637
+ if (failedSnap) {
638
+ hints.unshift(HELP_UPDATE_SNAP);
639
+ } else {
640
+ hints.push(HELP_QUITE);
641
+ }
642
+ this.ctx.logger.log(BADGE_PADDING + hints.join(c.dim(", ")));
643
+ if (this._lastRunCount) {
644
+ const LAST_RUN_TEXT = `rerun x${this._lastRunCount}`;
645
+ const LAST_RUN_TEXTS = [
646
+ c.blue(LAST_RUN_TEXT),
647
+ c.gray(LAST_RUN_TEXT),
648
+ c.dim(c.gray(LAST_RUN_TEXT))
649
+ ];
650
+ this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[0]);
651
+ this._lastRunTimeout = 0;
652
+ this._lastRunTimer = setInterval(() => {
653
+ this._lastRunTimeout += 1;
654
+ if (this._lastRunTimeout >= LAST_RUN_TEXTS.length) {
655
+ this.resetLastRunLog();
656
+ } else {
657
+ this.ctx.logger.logUpdate(
658
+ BADGE_PADDING + LAST_RUN_TEXTS[this._lastRunTimeout]
659
+ );
660
+ }
661
+ }, LAST_RUN_LOG_TIMEOUT / LAST_RUN_TEXTS.length);
662
+ }
663
+ }
664
+ resetLastRunLog() {
285
665
  clearInterval(this._lastRunTimer);
286
666
  this._lastRunTimer = void 0;
287
667
  this.ctx.logger.logUpdate.clear();
@@ -430,13 +810,6 @@ ${log.content}`);
430
810
  0
431
811
  );
432
812
  const threadTime = collectTime + testsTime + setupTime;
433
- const padTitle = (str) => c.dim(`${str.padStart(11)} `);
434
- const time = (time2) => {
435
- if (time2 > 1e3) {
436
- return `${(time2 / 1e3).toFixed(2)}s`;
437
- }
438
- return `${Math.round(time2)}ms`;
439
- };
440
813
  const snapshotOutput = renderSnapshotSummary(
441
814
  this.ctx.config.root,
442
815
  this.ctx.snapshot.summary
@@ -620,6 +993,15 @@ ${c.cyan(c.inverse(c.bold(" BENCH ")))} ${c.cyan("Summary")}
620
993
  };
621
994
  }
622
995
  }
996
+ function padTitle(str) {
997
+ return c.dim(`${str.padStart(11)} `);
998
+ }
999
+ function time(time2) {
1000
+ if (time2 > 1e3) {
1001
+ return `${(time2 / 1e3).toFixed(2)}s`;
1002
+ }
1003
+ return `${Math.round(time2)}ms`;
1004
+ }
623
1005
 
624
1006
  class BasicReporter extends BaseReporter {
625
1007
  constructor() {
@@ -1593,6 +1975,10 @@ function renderTree$1(tasks, options, level = 0, maxRows) {
1593
1975
  if (level === 0 && task.type === "suite" && "projectName" in task) {
1594
1976
  prefix += formatProjectName(task.projectName);
1595
1977
  }
1978
+ if (level === 0 && task.type === "suite" && task.meta.typecheck) {
1979
+ prefix += c.bgBlue(c.bold(" TS "));
1980
+ prefix += " ";
1981
+ }
1596
1982
  if (task.type === "test" && task.result?.retryCount && task.result.retryCount > 0) {
1597
1983
  suffix += c.yellow(` (retry x${task.result.retryCount})`);
1598
1984
  }
@@ -2519,11 +2905,11 @@ if (!processOk(process$1)) {
2519
2905
  return function () {}
2520
2906
  };
2521
2907
  } else {
2522
- var assert = require$$0$1;
2908
+ var assert = require$$0;
2523
2909
  var signals = requireSignals();
2524
2910
  var isWin = /^win/i.test(process$1.platform);
2525
2911
 
2526
- var EE = require$$0;
2912
+ var EE = require$$2;
2527
2913
  /* istanbul ignore if */
2528
2914
  if (typeof EE !== 'function') {
2529
2915
  EE = EE.EventEmitter;
@@ -3908,326 +4294,77 @@ function printStack(logger, project, stack, highlight, errorProperties, onStack)
3908
4294
  ].filter(Boolean).join(" ")}`
3909
4295
  )
3910
4296
  );
3911
- onStack?.(frame);
3912
- }
3913
- if (stack.length) {
3914
- logger.error();
3915
- }
3916
- if (hasProperties(errorProperties)) {
3917
- logger.error(c.red(c.dim(divider())));
3918
- const propertiesString = inspect(errorProperties);
3919
- logger.error(c.red(c.bold("Serialized Error:")), c.gray(propertiesString));
3920
- }
3921
- }
3922
- function hasProperties(obj) {
3923
- for (const _key in obj) {
3924
- return true;
3925
- }
3926
- return false;
3927
- }
3928
- function generateCodeFrame(source, indent = 0, loc, range = 2) {
3929
- const start = typeof loc === "object" ? positionToOffset(source, loc.line, loc.column) : loc;
3930
- const end = start;
3931
- const lines = source.split(lineSplitRE);
3932
- const nl = /\r\n/.test(source) ? 2 : 1;
3933
- let count = 0;
3934
- let res = [];
3935
- const columns = process.stdout?.columns || 80;
3936
- function lineNo(no = "") {
3937
- return c.gray(`${String(no).padStart(3, " ")}| `);
3938
- }
3939
- for (let i = 0; i < lines.length; i++) {
3940
- count += lines[i].length + nl;
3941
- if (count >= start) {
3942
- for (let j = i - range; j <= i + range || end > count; j++) {
3943
- if (j < 0 || j >= lines.length) {
3944
- continue;
3945
- }
3946
- const lineLength = lines[j].length;
3947
- if (stripAnsi(lines[j]).length > 200) {
3948
- return "";
3949
- }
3950
- res.push(
3951
- lineNo(j + 1) + cliTruncate(lines[j].replace(/\t/g, " "), columns - 5 - indent)
3952
- );
3953
- if (j === i) {
3954
- const pad = start - (count - lineLength) + (nl - 1);
3955
- const length = Math.max(
3956
- 1,
3957
- end > count ? lineLength - pad : end - start
3958
- );
3959
- res.push(lineNo() + " ".repeat(pad) + c.red("^".repeat(length)));
3960
- } else if (j > i) {
3961
- if (end > count) {
3962
- const length = Math.max(1, Math.min(end - count, lineLength));
3963
- res.push(lineNo() + c.red("^".repeat(length)));
3964
- }
3965
- count += lineLength + 1;
3966
- }
3967
- }
3968
- break;
3969
- }
3970
- }
3971
- if (indent) {
3972
- res = res.map((line) => " ".repeat(indent) + line);
3973
- }
3974
- return res.join("\n");
3975
- }
3976
-
3977
- function flattenTasks$1(task, baseName = "") {
3978
- const base = baseName ? `${baseName} > ` : "";
3979
- if (task.type === "suite") {
3980
- return task.tasks.flatMap(
3981
- (child) => flattenTasks$1(child, `${base}${task.name}`)
3982
- );
3983
- } else {
3984
- return [
3985
- {
3986
- ...task,
3987
- name: `${base}${task.name}`
3988
- }
3989
- ];
3990
- }
3991
- }
3992
- function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
3993
- let regex = /([\0-\x08\v\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g;
3994
- value = String(value || "").replace(regex, "");
3995
- {
3996
- regex = new RegExp(
3997
- /* eslint-disable regexp/prefer-character-class, regexp/no-obscure-range, regexp/no-useless-non-capturing-group */
3998
- "([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\uDFFE\\uDFFF])|\\uD8BF[\\uDFFE\\uDFFF]|\\uD8FF[\\uDFFE\\uDFFF]|(?:\\uD93F[\\uDFFE\\uDFFF])|\\uD97F[\\uDFFE\\uDFFF]|\\uD9BF[\\uDFFE\\uDFFF]|\\uD9FF[\\uDFFE\\uDFFF]|\\uDA3F[\\uDFFE\\uDFFF]|\\uDA7F[\\uDFFE\\uDFFF]|\\uDABF[\\uDFFE\\uDFFF]|(?:\\uDAFF[\\uDFFE\\uDFFF])|\\uDB3F[\\uDFFE\\uDFFF]|\\uDB7F[\\uDFFE\\uDFFF]|(?:\\uDBBF[\\uDFFE\\uDFFF])|\\uDBFF[\\uDFFE\\uDFFF](?:[\\0-\\t\\v\\f\\x0E-\\u2027\\u202A-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))",
3999
- "g"
4000
- /* eslint-enable */
4001
- );
4002
- value = value.replace(regex, "");
4003
- }
4004
- return value;
4005
- }
4006
- function escapeXML(value) {
4007
- return removeInvalidXMLCharacters(
4008
- String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;"));
4009
- }
4010
- function executionTime(durationMS) {
4011
- return (durationMS / 1e3).toLocaleString("en-US", {
4012
- useGrouping: false,
4013
- maximumFractionDigits: 10
4014
- });
4015
- }
4016
- function getDuration(task) {
4017
- const duration = task.result?.duration ?? 0;
4018
- return executionTime(duration);
4019
- }
4020
- class JUnitReporter {
4021
- ctx;
4022
- reportFile;
4023
- baseLog;
4024
- logger;
4025
- _timeStart = /* @__PURE__ */ new Date();
4026
- fileFd;
4027
- options;
4028
- constructor(options) {
4029
- this.options = { ...options };
4030
- this.options.includeConsoleOutput ??= true;
4031
- }
4032
- async onInit(ctx) {
4033
- this.ctx = ctx;
4034
- const outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "junit");
4035
- if (outputFile) {
4036
- this.reportFile = resolve(this.ctx.config.root, outputFile);
4037
- const outputDirectory = dirname(this.reportFile);
4038
- if (!existsSync(outputDirectory)) {
4039
- await promises.mkdir(outputDirectory, { recursive: true });
4040
- }
4041
- const fileFd = await promises.open(this.reportFile, "w+");
4042
- this.fileFd = fileFd;
4043
- this.baseLog = async (text) => {
4044
- if (!this.fileFd) {
4045
- this.fileFd = await promises.open(this.reportFile, "w+");
4046
- }
4047
- await promises.writeFile(this.fileFd, `${text}
4048
- `);
4049
- };
4050
- } else {
4051
- this.baseLog = async (text) => this.ctx.logger.log(text);
4052
- }
4053
- this._timeStart = /* @__PURE__ */ new Date();
4054
- this.logger = new IndentedLogger(this.baseLog);
4055
- }
4056
- async writeElement(name, attrs, children) {
4057
- const pairs = [];
4058
- for (const key in attrs) {
4059
- const attr = attrs[key];
4060
- if (attr === void 0) {
4061
- continue;
4062
- }
4063
- pairs.push(`${key}="${escapeXML(attr)}"`);
4064
- }
4065
- await this.logger.log(
4066
- `<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`
4067
- );
4068
- this.logger.indent();
4069
- await children.call(this);
4070
- this.logger.unindent();
4071
- await this.logger.log(`</${name}>`);
4072
- }
4073
- async writeLogs(task, type) {
4074
- if (task.logs == null || task.logs.length === 0) {
4075
- return;
4076
- }
4077
- const logType = type === "err" ? "stderr" : "stdout";
4078
- const logs = task.logs.filter((log) => log.type === logType);
4079
- if (logs.length === 0) {
4080
- return;
4081
- }
4082
- await this.writeElement(`system-${type}`, {}, async () => {
4083
- for (const log of logs) {
4084
- await this.baseLog(escapeXML(log.content));
4085
- }
4086
- });
4087
- }
4088
- async writeTasks(tasks, filename) {
4089
- for (const task of tasks) {
4090
- await this.writeElement(
4091
- "testcase",
4092
- {
4093
- classname: this.options.classname ?? filename,
4094
- file: this.options.addFileAttribute ? filename : void 0,
4095
- name: task.name,
4096
- time: getDuration(task)
4097
- },
4098
- async () => {
4099
- if (this.options.includeConsoleOutput) {
4100
- await this.writeLogs(task, "out");
4101
- await this.writeLogs(task, "err");
4102
- }
4103
- if (task.mode === "skip" || task.mode === "todo") {
4104
- await this.logger.log("<skipped/>");
4105
- }
4106
- if (task.result?.state === "fail") {
4107
- const errors = task.result.errors || [];
4108
- for (const error of errors) {
4109
- await this.writeElement(
4110
- "failure",
4111
- {
4112
- message: error?.message,
4113
- type: error?.name ?? error?.nameStr
4114
- },
4115
- async () => {
4116
- if (!error) {
4117
- return;
4118
- }
4119
- const result = capturePrintError(
4120
- error,
4121
- this.ctx,
4122
- { project: this.ctx.getProjectByTaskId(task.id), task }
4123
- );
4124
- await this.baseLog(
4125
- escapeXML(stripAnsi(result.output.trim()))
4126
- );
4127
- }
4128
- );
4129
- }
4130
- }
4131
- }
4132
- );
4133
- }
4134
- }
4135
- async onFinished(files = this.ctx.state.getFiles()) {
4136
- await this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
4137
- const transformed = files.map((file) => {
4138
- const tasks = file.tasks.flatMap((task) => flattenTasks$1(task));
4139
- const stats2 = tasks.reduce(
4140
- (stats3, task) => {
4141
- return {
4142
- passed: stats3.passed + Number(task.result?.state === "pass"),
4143
- failures: stats3.failures + Number(task.result?.state === "fail"),
4144
- skipped: stats3.skipped + Number(task.mode === "skip" || task.mode === "todo")
4145
- };
4146
- },
4147
- {
4148
- passed: 0,
4149
- failures: 0,
4150
- skipped: 0
4297
+ onStack?.(frame);
4298
+ }
4299
+ if (stack.length) {
4300
+ logger.error();
4301
+ }
4302
+ if (hasProperties(errorProperties)) {
4303
+ logger.error(c.red(c.dim(divider())));
4304
+ const propertiesString = inspect(errorProperties);
4305
+ logger.error(c.red(c.bold("Serialized Error:")), c.gray(propertiesString));
4306
+ }
4307
+ }
4308
+ function hasProperties(obj) {
4309
+ for (const _key in obj) {
4310
+ return true;
4311
+ }
4312
+ return false;
4313
+ }
4314
+ function generateCodeFrame(source, indent = 0, loc, range = 2) {
4315
+ const start = typeof loc === "object" ? positionToOffset(source, loc.line, loc.column) : loc;
4316
+ const end = start;
4317
+ const lines = source.split(lineSplitRE);
4318
+ const nl = /\r\n/.test(source) ? 2 : 1;
4319
+ let count = 0;
4320
+ let res = [];
4321
+ const columns = process.stdout?.columns || 80;
4322
+ for (let i = 0; i < lines.length; i++) {
4323
+ count += lines[i].length + nl;
4324
+ if (count >= start) {
4325
+ for (let j = i - range; j <= i + range || end > count; j++) {
4326
+ if (j < 0 || j >= lines.length) {
4327
+ continue;
4151
4328
  }
4152
- );
4153
- const suites = getSuites(file);
4154
- for (const suite of suites) {
4155
- if (suite.result?.errors) {
4156
- tasks.push(suite);
4157
- stats2.failures += 1;
4329
+ const lineLength = lines[j].length;
4330
+ if (stripAnsi(lines[j]).length > 200) {
4331
+ return "";
4158
4332
  }
4159
- }
4160
- if (tasks.length === 0 && file.result?.state === "fail") {
4161
- stats2.failures = 1;
4162
- tasks.push({
4163
- id: file.id,
4164
- type: "test",
4165
- name: file.name,
4166
- mode: "run",
4167
- result: file.result,
4168
- meta: {},
4169
- // NOTE: not used in JUnitReporter
4170
- context: null,
4171
- suite: null,
4172
- file: null
4173
- });
4174
- }
4175
- return {
4176
- ...file,
4177
- tasks,
4178
- stats: stats2
4179
- };
4180
- });
4181
- const stats = transformed.reduce(
4182
- (stats2, file) => {
4183
- stats2.tests += file.tasks.length;
4184
- stats2.failures += file.stats.failures;
4185
- return stats2;
4186
- },
4187
- {
4188
- name: this.options.suiteName || "vitest tests",
4189
- tests: 0,
4190
- failures: 0,
4191
- errors: 0,
4192
- // we cannot detect those
4193
- time: executionTime((/* @__PURE__ */ new Date()).getTime() - this._timeStart.getTime())
4194
- }
4195
- );
4196
- await this.writeElement("testsuites", stats, async () => {
4197
- for (const file of transformed) {
4198
- const filename = relative(this.ctx.config.root, file.filepath);
4199
- await this.writeElement(
4200
- "testsuite",
4201
- {
4202
- name: filename,
4203
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4204
- hostname: hostname(),
4205
- tests: file.tasks.length,
4206
- failures: file.stats.failures,
4207
- errors: 0,
4208
- // An errored test is one that had an unanticipated problem. We cannot detect those.
4209
- skipped: file.stats.skipped,
4210
- time: getDuration(file)
4211
- },
4212
- async () => {
4213
- await this.writeTasks(file.tasks, filename);
4214
- }
4333
+ res.push(
4334
+ lineNo(j + 1) + cliTruncate(lines[j].replace(/\t/g, " "), columns - 5 - indent)
4215
4335
  );
4336
+ if (j === i) {
4337
+ const pad = start - (count - lineLength) + (nl - 1);
4338
+ const length = Math.max(
4339
+ 1,
4340
+ end > count ? lineLength - pad : end - start
4341
+ );
4342
+ res.push(lineNo() + " ".repeat(pad) + c.red("^".repeat(length)));
4343
+ } else if (j > i) {
4344
+ if (end > count) {
4345
+ const length = Math.max(1, Math.min(end - count, lineLength));
4346
+ res.push(lineNo() + c.red("^".repeat(length)));
4347
+ }
4348
+ count += lineLength + 1;
4349
+ }
4216
4350
  }
4217
- });
4218
- if (this.reportFile) {
4219
- this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
4351
+ break;
4220
4352
  }
4221
- await this.fileFd?.close();
4222
- this.fileFd = void 0;
4223
4353
  }
4354
+ if (indent) {
4355
+ res = res.map((line) => " ".repeat(indent) + line);
4356
+ }
4357
+ return res.join("\n");
4358
+ }
4359
+ function lineNo(no = "") {
4360
+ return c.gray(`${String(no).padStart(3, " ")}| `);
4224
4361
  }
4225
4362
 
4226
- function flattenTasks(task, baseName = "") {
4363
+ function flattenTasks$1(task, baseName = "") {
4227
4364
  const base = baseName ? `${baseName} > ` : "";
4228
- if (task.type === "suite" && task.tasks.length > 0) {
4365
+ if (task.type === "suite") {
4229
4366
  return task.tasks.flatMap(
4230
- (child) => flattenTasks(child, `${base}${task.name}`)
4367
+ (child) => flattenTasks$1(child, `${base}${task.name}`)
4231
4368
  );
4232
4369
  } else {
4233
4370
  return [
@@ -4238,600 +4375,468 @@ function flattenTasks(task, baseName = "") {
4238
4375
  ];
4239
4376
  }
4240
4377
  }
4241
- class TapFlatReporter extends TapReporter {
4242
- onInit(ctx) {
4243
- super.onInit(ctx);
4244
- }
4245
- onFinished(files = this.ctx.state.getFiles()) {
4246
- this.ctx.logger.log("TAP version 13");
4247
- const flatTasks = files.flatMap((task) => flattenTasks(task));
4248
- this.logTasks(flatTasks);
4249
- }
4250
- }
4251
-
4252
- class HangingProcessReporter {
4253
- whyRunning;
4254
- onInit() {
4255
- const _require = createRequire(import.meta.url);
4256
- this.whyRunning = _require("why-is-node-running");
4257
- }
4258
- onProcessTimeout() {
4259
- this.whyRunning?.();
4378
+ function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
4379
+ let regex = /([\0-\x08\v\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g;
4380
+ value = String(value || "").replace(regex, "");
4381
+ {
4382
+ regex = new RegExp(
4383
+ /* eslint-disable regexp/prefer-character-class, regexp/no-obscure-range, regexp/no-useless-non-capturing-group */
4384
+ "([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\uDFFE\\uDFFF])|\\uD8BF[\\uDFFE\\uDFFF]|\\uD8FF[\\uDFFE\\uDFFF]|(?:\\uD93F[\\uDFFE\\uDFFF])|\\uD97F[\\uDFFE\\uDFFF]|\\uD9BF[\\uDFFE\\uDFFF]|\\uD9FF[\\uDFFE\\uDFFF]|\\uDA3F[\\uDFFE\\uDFFF]|\\uDA7F[\\uDFFE\\uDFFF]|\\uDABF[\\uDFFE\\uDFFF]|(?:\\uDAFF[\\uDFFE\\uDFFF])|\\uDB3F[\\uDFFE\\uDFFF]|\\uDB7F[\\uDFFE\\uDFFF]|(?:\\uDBBF[\\uDFFE\\uDFFF])|\\uDBFF[\\uDFFE\\uDFFF](?:[\\0-\\t\\v\\f\\x0E-\\u2027\\u202A-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))",
4385
+ "g"
4386
+ /* eslint-enable */
4387
+ );
4388
+ value = value.replace(regex, "");
4260
4389
  }
4390
+ return value;
4261
4391
  }
4262
-
4263
- class GithubActionsReporter {
4264
- ctx = void 0;
4265
- onInit(ctx) {
4266
- this.ctx = ctx;
4267
- }
4268
- onFinished(files = [], errors = []) {
4269
- const projectErrors = new Array();
4270
- for (const error of errors) {
4271
- projectErrors.push({
4272
- project: this.ctx.getCoreWorkspaceProject(),
4273
- title: "Unhandled error",
4274
- error
4275
- });
4276
- }
4277
- for (const file of files) {
4278
- const tasks = getTasks(file);
4279
- const project = this.ctx.getProjectByTaskId(file.id);
4280
- for (const task of tasks) {
4281
- if (task.result?.state !== "fail") {
4282
- continue;
4283
- }
4284
- const title = getFullName(task, " > ");
4285
- for (const error of task.result?.errors ?? []) {
4286
- projectErrors.push({
4287
- project,
4288
- title,
4289
- error,
4290
- file
4291
- });
4292
- }
4293
- }
4294
- }
4295
- for (const { project, title, error, file } of projectErrors) {
4296
- const result = capturePrintError(error, this.ctx, { project, task: file });
4297
- const stack = result?.nearest;
4298
- if (!stack) {
4299
- continue;
4300
- }
4301
- const formatted = formatMessage({
4302
- command: "error",
4303
- properties: {
4304
- file: stack.file,
4305
- title,
4306
- line: String(stack.line),
4307
- column: String(stack.column)
4308
- },
4309
- message: stripAnsi(result.output)
4310
- });
4311
- this.ctx.logger.log(`
4312
- ${formatted}`);
4313
- }
4314
- }
4392
+ function escapeXML(value) {
4393
+ return removeInvalidXMLCharacters(
4394
+ String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;"));
4315
4395
  }
4316
- function formatMessage({
4317
- command,
4318
- properties,
4319
- message
4320
- }) {
4321
- let result = `::${command}`;
4322
- Object.entries(properties).forEach(([k, v], i) => {
4323
- result += i === 0 ? " " : ",";
4324
- result += `${k}=${escapeProperty(v)}`;
4396
+ function executionTime(durationMS) {
4397
+ return (durationMS / 1e3).toLocaleString("en-US", {
4398
+ useGrouping: false,
4399
+ maximumFractionDigits: 10
4325
4400
  });
4326
- result += `::${escapeData(message)}`;
4327
- return result;
4328
- }
4329
- function escapeData(s) {
4330
- return s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
4331
4401
  }
4332
- function escapeProperty(s) {
4333
- return s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A").replace(/:/g, "%3A").replace(/,/g, "%2C");
4402
+ function getDuration(task) {
4403
+ const duration = task.result?.duration ?? 0;
4404
+ return executionTime(duration);
4334
4405
  }
4335
-
4336
- class BlobReporter {
4406
+ class JUnitReporter {
4337
4407
  ctx;
4408
+ reportFile;
4409
+ baseLog;
4410
+ logger;
4411
+ _timeStart = /* @__PURE__ */ new Date();
4412
+ fileFd;
4338
4413
  options;
4339
4414
  constructor(options) {
4340
- this.options = options;
4341
- }
4342
- onInit(ctx) {
4343
- if (ctx.config.watch) {
4344
- throw new Error("Blob reporter is not supported in watch mode");
4345
- }
4346
- this.ctx = ctx;
4415
+ this.options = { ...options };
4416
+ this.options.includeConsoleOutput ??= true;
4347
4417
  }
4348
- async onFinished(files = [], errors = [], coverage) {
4349
- let outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "blob");
4350
- if (!outputFile) {
4351
- const shard = this.ctx.config.shard;
4352
- outputFile = shard ? `.vitest-reports/blob-${shard.index}-${shard.count}.json` : ".vitest-reports/blob.json";
4353
- }
4354
- const moduleKeys = this.ctx.projects.map(
4355
- (project) => {
4356
- return [
4357
- project.getName(),
4358
- [...project.server.moduleGraph.idToModuleMap.keys()]
4359
- ];
4418
+ async onInit(ctx) {
4419
+ this.ctx = ctx;
4420
+ const outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "junit");
4421
+ if (outputFile) {
4422
+ this.reportFile = resolve(this.ctx.config.root, outputFile);
4423
+ const outputDirectory = dirname(this.reportFile);
4424
+ if (!existsSync(outputDirectory)) {
4425
+ await promises.mkdir(outputDirectory, { recursive: true });
4360
4426
  }
4361
- );
4362
- const report = stringify([
4363
- this.ctx.version,
4364
- files,
4365
- errors,
4366
- moduleKeys,
4367
- coverage
4368
- ]);
4369
- const reportFile = resolve(this.ctx.config.root, outputFile);
4370
- const dir = dirname(reportFile);
4371
- if (!existsSync(dir)) {
4372
- await mkdir(dir, { recursive: true });
4427
+ const fileFd = await promises.open(this.reportFile, "w+");
4428
+ this.fileFd = fileFd;
4429
+ this.baseLog = async (text) => {
4430
+ if (!this.fileFd) {
4431
+ this.fileFd = await promises.open(this.reportFile, "w+");
4432
+ }
4433
+ await promises.writeFile(this.fileFd, `${text}
4434
+ `);
4435
+ };
4436
+ } else {
4437
+ this.baseLog = async (text) => this.ctx.logger.log(text);
4373
4438
  }
4374
- await writeFile(reportFile, report, "utf-8");
4375
- this.ctx.logger.log("blob report written to", reportFile);
4439
+ this._timeStart = /* @__PURE__ */ new Date();
4440
+ this.logger = new IndentedLogger(this.baseLog);
4376
4441
  }
4377
- }
4378
- async function readBlobs(blobsDirectory, projectsArray) {
4379
- const resolvedDir = resolve(process.cwd(), blobsDirectory);
4380
- const blobsFiles = await readdir(resolvedDir);
4381
- const promises = blobsFiles.map(async (filename) => {
4382
- const fullPath = resolve(resolvedDir, filename);
4383
- const stats = await stat(fullPath);
4384
- if (!stats.isFile()) {
4385
- throw new TypeError(
4386
- `vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`
4387
- );
4388
- }
4389
- const content = await readFile(fullPath, "utf-8");
4390
- const [version, files2, errors2, moduleKeys, coverage] = parse(
4391
- content
4392
- );
4393
- if (!version) {
4394
- throw new TypeError(
4395
- `vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`
4396
- );
4442
+ async writeElement(name, attrs, children) {
4443
+ const pairs = [];
4444
+ for (const key in attrs) {
4445
+ const attr = attrs[key];
4446
+ if (attr === void 0) {
4447
+ continue;
4448
+ }
4449
+ pairs.push(`${key}="${escapeXML(attr)}"`);
4397
4450
  }
4398
- return { version, files: files2, errors: errors2, moduleKeys, coverage, file: filename };
4399
- });
4400
- const blobs = await Promise.all(promises);
4401
- if (!blobs.length) {
4402
- throw new Error(
4403
- `vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`
4404
- );
4405
- }
4406
- const versions = new Set(blobs.map((blob) => blob.version));
4407
- if (versions.size > 1) {
4408
- throw new Error(
4409
- `vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received
4410
-
4411
- ${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`
4451
+ await this.logger.log(
4452
+ `<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`
4412
4453
  );
4454
+ this.logger.indent();
4455
+ await children.call(this);
4456
+ this.logger.unindent();
4457
+ await this.logger.log(`</${name}>`);
4413
4458
  }
4414
- const projects = Object.fromEntries(
4415
- projectsArray.map((p) => [p.getName(), p])
4416
- );
4417
- blobs.forEach((blob) => {
4418
- blob.moduleKeys.forEach(([projectName, moduleIds]) => {
4419
- const project = projects[projectName];
4420
- if (!project) {
4421
- return;
4459
+ async writeLogs(task, type) {
4460
+ if (task.logs == null || task.logs.length === 0) {
4461
+ return;
4462
+ }
4463
+ const logType = type === "err" ? "stderr" : "stdout";
4464
+ const logs = task.logs.filter((log) => log.type === logType);
4465
+ if (logs.length === 0) {
4466
+ return;
4467
+ }
4468
+ await this.writeElement(`system-${type}`, {}, async () => {
4469
+ for (const log of logs) {
4470
+ await this.baseLog(escapeXML(log.content));
4422
4471
  }
4423
- moduleIds.forEach((moduleId) => {
4424
- project.server.moduleGraph.idToModuleMap.set(moduleId, {
4425
- id: moduleId,
4426
- url: moduleId,
4427
- file: cleanUrl(moduleId),
4428
- ssrTransformResult: null,
4429
- transformResult: null,
4430
- importedBindings: null,
4431
- importedModules: /* @__PURE__ */ new Set(),
4432
- importers: /* @__PURE__ */ new Set(),
4433
- type: "js",
4434
- clientImportedModules: /* @__PURE__ */ new Set(),
4435
- ssrError: null,
4436
- ssrImportedModules: /* @__PURE__ */ new Set(),
4437
- ssrModule: null,
4438
- acceptedHmrDeps: /* @__PURE__ */ new Set(),
4439
- acceptedHmrExports: null,
4440
- lastHMRTimestamp: 0,
4441
- lastInvalidationTimestamp: 0
4442
- });
4443
- });
4444
4472
  });
4445
- });
4446
- const files = blobs.flatMap((blob) => blob.files).sort((f1, f2) => {
4447
- const time1 = f1.result?.startTime || 0;
4448
- const time2 = f2.result?.startTime || 0;
4449
- return time1 - time2;
4450
- });
4451
- const errors = blobs.flatMap((blob) => blob.errors);
4452
- const coverages = blobs.map((blob) => blob.coverage);
4453
- return {
4454
- files,
4455
- errors,
4456
- coverages
4457
- };
4458
- }
4459
-
4460
- class TestProject {
4461
- /**
4462
- * The global vitest instance.
4463
- * @experimental The public Vitest API is experimental and does not follow semver.
4464
- */
4465
- vitest;
4466
- /**
4467
- * The workspace project this test project is associated with.
4468
- * @experimental The public Vitest API is experimental and does not follow semver.
4469
- */
4470
- workspaceProject;
4471
- /**
4472
- * Resolved project configuration.
4473
- */
4474
- config;
4475
- /**
4476
- * Resolved global configuration. If there are no workspace projects, this will be the same as `config`.
4477
- */
4478
- globalConfig;
4479
- /**
4480
- * The name of the project or an empty string if not set.
4481
- */
4482
- name;
4483
- constructor(workspaceProject) {
4484
- this.workspaceProject = workspaceProject;
4485
- this.vitest = workspaceProject.ctx;
4486
- this.globalConfig = workspaceProject.ctx.config;
4487
- this.config = workspaceProject.config;
4488
- this.name = workspaceProject.getName();
4489
- }
4490
- /**
4491
- * Serialized project configuration. This is the config that tests receive.
4492
- */
4493
- get serializedConfig() {
4494
- return this.workspaceProject.getSerializableConfig();
4495
- }
4496
- /**
4497
- * Custom context provided to the project.
4498
- */
4499
- context() {
4500
- return this.workspaceProject.getProvidedContext();
4501
4473
  }
4502
- /**
4503
- * Provide a custom serializable context to the project. This context will be available for tests once they run.
4504
- */
4505
- provide(key, value) {
4506
- this.workspaceProject.provide(key, value);
4474
+ async writeTasks(tasks, filename) {
4475
+ for (const task of tasks) {
4476
+ await this.writeElement(
4477
+ "testcase",
4478
+ {
4479
+ classname: this.options.classname ?? filename,
4480
+ file: this.options.addFileAttribute ? filename : void 0,
4481
+ name: task.name,
4482
+ time: getDuration(task)
4483
+ },
4484
+ async () => {
4485
+ if (this.options.includeConsoleOutput) {
4486
+ await this.writeLogs(task, "out");
4487
+ await this.writeLogs(task, "err");
4488
+ }
4489
+ if (task.mode === "skip" || task.mode === "todo") {
4490
+ await this.logger.log("<skipped/>");
4491
+ }
4492
+ if (task.result?.state === "fail") {
4493
+ const errors = task.result.errors || [];
4494
+ for (const error of errors) {
4495
+ await this.writeElement(
4496
+ "failure",
4497
+ {
4498
+ message: error?.message,
4499
+ type: error?.name ?? error?.nameStr
4500
+ },
4501
+ async () => {
4502
+ if (!error) {
4503
+ return;
4504
+ }
4505
+ const result = capturePrintError(
4506
+ error,
4507
+ this.ctx,
4508
+ { project: this.ctx.getProjectByTaskId(task.id), task }
4509
+ );
4510
+ await this.baseLog(
4511
+ escapeXML(stripAnsi(result.output.trim()))
4512
+ );
4513
+ }
4514
+ );
4515
+ }
4516
+ }
4517
+ }
4518
+ );
4519
+ }
4507
4520
  }
4508
- toJSON() {
4509
- return {
4510
- name: this.name,
4511
- serializedConfig: this.serializedConfig,
4512
- context: this.context()
4513
- };
4521
+ async onFinished(files = this.ctx.state.getFiles()) {
4522
+ await this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
4523
+ const transformed = files.map((file) => {
4524
+ const tasks = file.tasks.flatMap((task) => flattenTasks$1(task));
4525
+ const stats2 = tasks.reduce(
4526
+ (stats3, task) => {
4527
+ return {
4528
+ passed: stats3.passed + Number(task.result?.state === "pass"),
4529
+ failures: stats3.failures + Number(task.result?.state === "fail"),
4530
+ skipped: stats3.skipped + Number(task.mode === "skip" || task.mode === "todo")
4531
+ };
4532
+ },
4533
+ {
4534
+ passed: 0,
4535
+ failures: 0,
4536
+ skipped: 0
4537
+ }
4538
+ );
4539
+ const suites = getSuites(file);
4540
+ for (const suite of suites) {
4541
+ if (suite.result?.errors) {
4542
+ tasks.push(suite);
4543
+ stats2.failures += 1;
4544
+ }
4545
+ }
4546
+ if (tasks.length === 0 && file.result?.state === "fail") {
4547
+ stats2.failures = 1;
4548
+ tasks.push({
4549
+ id: file.id,
4550
+ type: "test",
4551
+ name: file.name,
4552
+ mode: "run",
4553
+ result: file.result,
4554
+ meta: {},
4555
+ // NOTE: not used in JUnitReporter
4556
+ context: null,
4557
+ suite: null,
4558
+ file: null
4559
+ });
4560
+ }
4561
+ return {
4562
+ ...file,
4563
+ tasks,
4564
+ stats: stats2
4565
+ };
4566
+ });
4567
+ const stats = transformed.reduce(
4568
+ (stats2, file) => {
4569
+ stats2.tests += file.tasks.length;
4570
+ stats2.failures += file.stats.failures;
4571
+ return stats2;
4572
+ },
4573
+ {
4574
+ name: this.options.suiteName || "vitest tests",
4575
+ tests: 0,
4576
+ failures: 0,
4577
+ errors: 0,
4578
+ // we cannot detect those
4579
+ time: executionTime((/* @__PURE__ */ new Date()).getTime() - this._timeStart.getTime())
4580
+ }
4581
+ );
4582
+ await this.writeElement("testsuites", stats, async () => {
4583
+ for (const file of transformed) {
4584
+ const filename = relative(this.ctx.config.root, file.filepath);
4585
+ await this.writeElement(
4586
+ "testsuite",
4587
+ {
4588
+ name: filename,
4589
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4590
+ hostname: hostname(),
4591
+ tests: file.tasks.length,
4592
+ failures: file.stats.failures,
4593
+ errors: 0,
4594
+ // An errored test is one that had an unanticipated problem. We cannot detect those.
4595
+ skipped: file.stats.skipped,
4596
+ time: getDuration(file)
4597
+ },
4598
+ async () => {
4599
+ await this.writeTasks(file.tasks, filename);
4600
+ }
4601
+ );
4602
+ }
4603
+ });
4604
+ if (this.reportFile) {
4605
+ this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
4606
+ }
4607
+ await this.fileFd?.close();
4608
+ this.fileFd = void 0;
4514
4609
  }
4515
4610
  }
4516
4611
 
4517
- class ReportedTaskImplementation {
4518
- /**
4519
- * Task instance.
4520
- * @experimental Public runner task API is experimental and does not follow semver.
4521
- */
4522
- task;
4523
- /**
4524
- * The project assosiacted with the test or suite.
4525
- */
4526
- project;
4527
- /**
4528
- * Unique identifier.
4529
- * This ID is deterministic and will be the same for the same test across multiple runs.
4530
- * The ID is based on the project name, file path and test position.
4531
- */
4532
- id;
4533
- /**
4534
- * Location in the file where the test or suite is defined.
4535
- */
4536
- location;
4537
- constructor(task, project) {
4538
- this.task = task;
4539
- this.project = project.testProject || (project.testProject = new TestProject(project));
4540
- this.id = task.id;
4541
- this.location = task.location;
4542
- }
4543
- /**
4544
- * Creates a new reported task instance and stores it in the project's state for future use.
4545
- */
4546
- static register(task, project) {
4547
- const state = new this(task, project);
4548
- storeTask(project, task, state);
4549
- return state;
4612
+ function flattenTasks(task, baseName = "") {
4613
+ const base = baseName ? `${baseName} > ` : "";
4614
+ if (task.type === "suite" && task.tasks.length > 0) {
4615
+ return task.tasks.flatMap(
4616
+ (child) => flattenTasks(child, `${base}${task.name}`)
4617
+ );
4618
+ } else {
4619
+ return [
4620
+ {
4621
+ ...task,
4622
+ name: `${base}${task.name}`
4623
+ }
4624
+ ];
4550
4625
  }
4551
4626
  }
4552
- class TestCase extends ReportedTaskImplementation {
4553
- #fullName;
4554
- type = "test";
4555
- /**
4556
- * Direct reference to the test file where the test or suite is defined.
4557
- */
4558
- file;
4559
- /**
4560
- * Name of the test.
4561
- */
4562
- name;
4563
- /**
4564
- * Options that the test was initiated with.
4565
- */
4566
- options;
4567
- /**
4568
- * Parent suite. If the test was called directly inside the file, the parent will be the file.
4569
- */
4570
- parent;
4571
- constructor(task, project) {
4572
- super(task, project);
4573
- this.name = task.name;
4574
- this.file = getReportedTask(project, task.file);
4575
- const suite = this.task.suite;
4576
- if (suite) {
4577
- this.parent = getReportedTask(project, suite);
4578
- } else {
4579
- this.parent = this.file;
4580
- }
4581
- this.options = buildOptions(task);
4582
- }
4583
- /**
4584
- * Full name of the test including all parent suites separated with `>`.
4585
- */
4586
- get fullName() {
4587
- if (this.#fullName === void 0) {
4588
- this.#fullName = getTestName(this.task, " > ");
4589
- }
4590
- return this.#fullName;
4591
- }
4592
- /**
4593
- * Test results. Will be `undefined` if test is not finished yet or was just collected.
4594
- */
4595
- result() {
4596
- const result = this.task.result;
4597
- if (!result || result.state === "run") {
4598
- return void 0;
4599
- }
4600
- const state = result.state === "fail" ? "failed" : result.state === "pass" ? "passed" : "skipped";
4601
- return {
4602
- state,
4603
- errors: result.errors
4604
- };
4627
+ class TapFlatReporter extends TapReporter {
4628
+ onInit(ctx) {
4629
+ super.onInit(ctx);
4605
4630
  }
4606
- /**
4607
- * Checks if the test did not fail the suite.
4608
- * If the test is not finished yet or was skipped, it will return `true`.
4609
- */
4610
- ok() {
4611
- const result = this.result();
4612
- return !result || result.state !== "failed";
4631
+ onFinished(files = this.ctx.state.getFiles()) {
4632
+ this.ctx.logger.log("TAP version 13");
4633
+ const flatTasks = files.flatMap((task) => flattenTasks(task));
4634
+ this.logTasks(flatTasks);
4613
4635
  }
4614
- /**
4615
- * Custom metadata that was attached to the test during its execution.
4616
- */
4617
- meta() {
4618
- return this.task.meta;
4636
+ }
4637
+
4638
+ class HangingProcessReporter {
4639
+ whyRunning;
4640
+ onInit() {
4641
+ const _require = createRequire(import.meta.url);
4642
+ this.whyRunning = _require("why-is-node-running");
4619
4643
  }
4620
- /**
4621
- * Useful information about the test like duration, memory usage, etc.
4622
- * Diagnostic is only available after the test has finished.
4623
- */
4624
- diagnostic() {
4625
- const result = this.task.result;
4626
- if (!result || result.state === "run" || !result.startTime) {
4627
- return void 0;
4628
- }
4629
- return {
4630
- heap: result.heap,
4631
- duration: result.duration,
4632
- startTime: result.startTime,
4633
- retryCount: result.retryCount ?? 0,
4634
- repeatCount: result.repeatCount ?? 0,
4635
- flaky: !!result.retryCount && result.state === "pass" && result.retryCount > 0
4636
- };
4644
+ onProcessTimeout() {
4645
+ this.whyRunning?.();
4637
4646
  }
4638
4647
  }
4639
- class TestCollection {
4640
- #task;
4641
- #project;
4642
- constructor(task, project) {
4643
- this.#task = task;
4644
- this.#project = project;
4648
+
4649
+ class GithubActionsReporter {
4650
+ ctx = void 0;
4651
+ onInit(ctx) {
4652
+ this.ctx = ctx;
4645
4653
  }
4646
- /**
4647
- * Returns the test or suite at a specific index in the array.
4648
- */
4649
- at(index) {
4650
- if (index < 0) {
4651
- index = this.size + index;
4654
+ onFinished(files = [], errors = []) {
4655
+ const projectErrors = new Array();
4656
+ for (const error of errors) {
4657
+ projectErrors.push({
4658
+ project: this.ctx.getCoreWorkspaceProject(),
4659
+ title: "Unhandled error",
4660
+ error
4661
+ });
4652
4662
  }
4653
- return getReportedTask(this.#project, this.#task.tasks[index]);
4654
- }
4655
- /**
4656
- * The number of tests and suites in the collection.
4657
- */
4658
- get size() {
4659
- return this.#task.tasks.length;
4660
- }
4661
- /**
4662
- * Returns the collection in array form for easier manipulation.
4663
- */
4664
- array() {
4665
- return Array.from(this);
4666
- }
4667
- /**
4668
- * Filters all tests that are part of this collection and its children.
4669
- */
4670
- *allTests(state) {
4671
- for (const child of this) {
4672
- if (child.type === "suite") {
4673
- yield* child.children.allTests(state);
4674
- } else if (state) {
4675
- const testState = getTestState(child);
4676
- if (state === testState) {
4677
- yield child;
4663
+ for (const file of files) {
4664
+ const tasks = getTasks(file);
4665
+ const project = this.ctx.getProjectByTaskId(file.id);
4666
+ for (const task of tasks) {
4667
+ if (task.result?.state !== "fail") {
4668
+ continue;
4669
+ }
4670
+ const title = getFullName(task, " > ");
4671
+ for (const error of task.result?.errors ?? []) {
4672
+ projectErrors.push({
4673
+ project,
4674
+ title,
4675
+ error,
4676
+ file
4677
+ });
4678
4678
  }
4679
- } else {
4680
- yield child;
4681
4679
  }
4682
4680
  }
4683
- }
4684
- /**
4685
- * Filters only the tests that are part of this collection.
4686
- */
4687
- *tests(state) {
4688
- for (const child of this) {
4689
- if (child.type !== "test") {
4681
+ for (const { project, title, error, file } of projectErrors) {
4682
+ const result = capturePrintError(error, this.ctx, { project, task: file });
4683
+ const stack = result?.nearest;
4684
+ if (!stack) {
4690
4685
  continue;
4691
4686
  }
4692
- if (state) {
4693
- const testState = getTestState(child);
4694
- if (state === testState) {
4695
- yield child;
4696
- }
4697
- } else {
4698
- yield child;
4699
- }
4687
+ const formatted = formatMessage({
4688
+ command: "error",
4689
+ properties: {
4690
+ file: stack.file,
4691
+ title,
4692
+ line: String(stack.line),
4693
+ column: String(stack.column)
4694
+ },
4695
+ message: stripAnsi(result.output)
4696
+ });
4697
+ this.ctx.logger.log(`
4698
+ ${formatted}`);
4700
4699
  }
4701
4700
  }
4702
- /**
4703
- * Filters only the suites that are part of this collection.
4704
- */
4705
- *suites() {
4706
- for (const child of this) {
4707
- if (child.type === "suite") {
4708
- yield child;
4709
- }
4710
- }
4701
+ }
4702
+ function formatMessage({
4703
+ command,
4704
+ properties,
4705
+ message
4706
+ }) {
4707
+ let result = `::${command}`;
4708
+ Object.entries(properties).forEach(([k, v], i) => {
4709
+ result += i === 0 ? " " : ",";
4710
+ result += `${k}=${escapeProperty(v)}`;
4711
+ });
4712
+ result += `::${escapeData(message)}`;
4713
+ return result;
4714
+ }
4715
+ function escapeData(s) {
4716
+ return s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
4717
+ }
4718
+ function escapeProperty(s) {
4719
+ return s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A").replace(/:/g, "%3A").replace(/,/g, "%2C");
4720
+ }
4721
+
4722
+ class BlobReporter {
4723
+ ctx;
4724
+ options;
4725
+ constructor(options) {
4726
+ this.options = options;
4711
4727
  }
4712
- /**
4713
- * Filters all suites that are part of this collection and its children.
4714
- */
4715
- *allSuites() {
4716
- for (const child of this) {
4717
- if (child.type === "suite") {
4718
- yield child;
4719
- yield* child.children.allSuites();
4720
- }
4728
+ onInit(ctx) {
4729
+ if (ctx.config.watch) {
4730
+ throw new Error("Blob reporter is not supported in watch mode");
4721
4731
  }
4732
+ this.ctx = ctx;
4722
4733
  }
4723
- *[Symbol.iterator]() {
4724
- for (const task of this.#task.tasks) {
4725
- yield getReportedTask(this.#project, task);
4734
+ async onFinished(files = [], errors = [], coverage) {
4735
+ let outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "blob");
4736
+ if (!outputFile) {
4737
+ const shard = this.ctx.config.shard;
4738
+ outputFile = shard ? `.vitest-reports/blob-${shard.index}-${shard.count}.json` : ".vitest-reports/blob.json";
4726
4739
  }
4740
+ const modules = this.ctx.projects.map(
4741
+ (project) => {
4742
+ return [
4743
+ project.getName(),
4744
+ [...project.server.moduleGraph.idToModuleMap.entries()].map((mod) => {
4745
+ if (!mod[1].file) {
4746
+ return null;
4747
+ }
4748
+ return [mod[0], mod[1].file, mod[1].url];
4749
+ }).filter((x) => x != null)
4750
+ ];
4751
+ }
4752
+ );
4753
+ const report = stringify([
4754
+ this.ctx.version,
4755
+ files,
4756
+ errors,
4757
+ modules,
4758
+ coverage
4759
+ ]);
4760
+ const reportFile = resolve(this.ctx.config.root, outputFile);
4761
+ const dir = dirname(reportFile);
4762
+ if (!existsSync(dir)) {
4763
+ await mkdir(dir, { recursive: true });
4764
+ }
4765
+ await writeFile(reportFile, report, "utf-8");
4766
+ this.ctx.logger.log("blob report written to", reportFile);
4727
4767
  }
4728
4768
  }
4729
- class SuiteImplementation extends ReportedTaskImplementation {
4730
- /**
4731
- * Collection of suites and tests that are part of this suite.
4732
- */
4733
- children;
4734
- constructor(task, project) {
4735
- super(task, project);
4736
- this.children = new TestCollection(task, project);
4737
- }
4738
- }
4739
- class TestSuite extends SuiteImplementation {
4740
- #fullName;
4741
- type = "suite";
4742
- /**
4743
- * Name of the test or the suite.
4744
- */
4745
- name;
4746
- /**
4747
- * Direct reference to the test file where the test or suite is defined.
4748
- */
4749
- file;
4750
- /**
4751
- * Parent suite. If suite was called directly inside the file, the parent will be the file.
4752
- */
4753
- parent;
4754
- /**
4755
- * Options that suite was initiated with.
4756
- */
4757
- options;
4758
- constructor(task, project) {
4759
- super(task, project);
4760
- this.name = task.name;
4761
- this.file = getReportedTask(project, task.file);
4762
- const suite = this.task.suite;
4763
- if (suite) {
4764
- this.parent = getReportedTask(project, suite);
4765
- } else {
4766
- this.parent = this.file;
4769
+ async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
4770
+ const resolvedDir = resolve(process.cwd(), blobsDirectory);
4771
+ const blobsFiles = await readdir(resolvedDir);
4772
+ const promises = blobsFiles.map(async (filename) => {
4773
+ const fullPath = resolve(resolvedDir, filename);
4774
+ const stats = await stat(fullPath);
4775
+ if (!stats.isFile()) {
4776
+ throw new TypeError(
4777
+ `vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`
4778
+ );
4767
4779
  }
4768
- this.options = buildOptions(task);
4769
- }
4770
- /**
4771
- * Full name of the suite including all parent suites separated with `>`.
4772
- */
4773
- get fullName() {
4774
- if (this.#fullName === void 0) {
4775
- this.#fullName = getTestName(this.task, " > ");
4780
+ const content = await readFile(fullPath, "utf-8");
4781
+ const [version, files2, errors2, moduleKeys, coverage] = parse(
4782
+ content
4783
+ );
4784
+ if (!version) {
4785
+ throw new TypeError(
4786
+ `vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`
4787
+ );
4776
4788
  }
4777
- return this.#fullName;
4789
+ return { version, files: files2, errors: errors2, moduleKeys, coverage, file: filename };
4790
+ });
4791
+ const blobs = await Promise.all(promises);
4792
+ if (!blobs.length) {
4793
+ throw new Error(
4794
+ `vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`
4795
+ );
4778
4796
  }
4779
- }
4780
- class TestFile extends SuiteImplementation {
4781
- type = "file";
4782
- /**
4783
- * This is usually an absolute UNIX file path.
4784
- * It can be a virtual id if the file is not on the disk.
4785
- * This value corresponds to Vite's `ModuleGraph` id.
4786
- */
4787
- moduleId;
4788
- constructor(task, project) {
4789
- super(task, project);
4790
- this.moduleId = task.filepath;
4797
+ const versions = new Set(blobs.map((blob) => blob.version));
4798
+ if (versions.size > 1) {
4799
+ throw new Error(
4800
+ `vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received
4801
+
4802
+ ${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`
4803
+ );
4791
4804
  }
4792
- /**
4793
- * Useful information about the file like duration, memory usage, etc.
4794
- * If the file was not executed yet, all diagnostic values will return `0`.
4795
- */
4796
- diagnostic() {
4797
- const setupDuration = this.task.setupDuration || 0;
4798
- const collectDuration = this.task.collectDuration || 0;
4799
- const prepareDuration = this.task.prepareDuration || 0;
4800
- const environmentSetupDuration = this.task.environmentLoad || 0;
4801
- const duration = this.task.result?.duration || 0;
4802
- return {
4803
- environmentSetupDuration,
4804
- prepareDuration,
4805
- collectDuration,
4806
- setupDuration,
4807
- duration
4808
- };
4805
+ if (!versions.has(currentVersion)) {
4806
+ throw new Error(
4807
+ `the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`
4808
+ );
4809
4809
  }
4810
- }
4811
- function buildOptions(task) {
4810
+ const projects = Object.fromEntries(
4811
+ projectsArray.map((p) => [p.getName(), p])
4812
+ );
4813
+ blobs.forEach((blob) => {
4814
+ blob.moduleKeys.forEach(([projectName, moduleIds]) => {
4815
+ const project = projects[projectName];
4816
+ if (!project) {
4817
+ return;
4818
+ }
4819
+ moduleIds.forEach(([moduleId, file, url]) => {
4820
+ const moduleNode = project.server.moduleGraph.createFileOnlyEntry(file);
4821
+ moduleNode.url = url;
4822
+ moduleNode.id = moduleId;
4823
+ project.server.moduleGraph.idToModuleMap.set(moduleId, moduleNode);
4824
+ });
4825
+ });
4826
+ });
4827
+ const files = blobs.flatMap((blob) => blob.files).sort((f1, f2) => {
4828
+ const time1 = f1.result?.startTime || 0;
4829
+ const time2 = f2.result?.startTime || 0;
4830
+ return time1 - time2;
4831
+ });
4832
+ const errors = blobs.flatMap((blob) => blob.errors);
4833
+ const coverages = blobs.map((blob) => blob.coverage);
4812
4834
  return {
4813
- each: task.each,
4814
- concurrent: task.concurrent,
4815
- shuffle: task.shuffle,
4816
- retry: task.retry,
4817
- repeats: task.repeats,
4818
- mode: task.mode
4835
+ files,
4836
+ errors,
4837
+ coverages
4819
4838
  };
4820
4839
  }
4821
- function getTestState(test) {
4822
- const result = test.result();
4823
- return result ? result.state : "running";
4824
- }
4825
- function storeTask(project, runnerTask, reportedTask) {
4826
- project.ctx.state.reportedTasksMap.set(runnerTask, reportedTask);
4827
- }
4828
- function getReportedTask(project, runnerTask) {
4829
- const reportedTask = project.ctx.state.getReportedEntity(runnerTask);
4830
- if (!reportedTask) {
4831
- throw new Error(`Task instance was not found for ${runnerTask.type} "${runnerTask.name}"`);
4832
- }
4833
- return reportedTask;
4834
- }
4835
4840
 
4836
4841
  const outputMap = /* @__PURE__ */ new WeakMap();
4837
4842
  function formatFilepath(path) {
@@ -4928,7 +4933,7 @@ function renderTree(tasks, options, level = 0, shallow = false) {
4928
4933
  if (baseline) {
4929
4934
  benchMap[t.id].baseline = {
4930
4935
  ...baseline,
4931
- samples: Array(baseline.sampleCount)
4936
+ samples: Array.from({ length: baseline.sampleCount })
4932
4937
  };
4933
4938
  }
4934
4939
  }
@@ -5217,6 +5222,7 @@ const BenchmarkReportsMap = {
5217
5222
  verbose: VerboseReporter
5218
5223
  };
5219
5224
 
5225
+ const TestFile = TestModule;
5220
5226
  const ReportersMap = {
5221
5227
  "default": DefaultReporter,
5222
5228
  "basic": BasicReporter,
@@ -5231,4 +5237,4 @@ const ReportersMap = {
5231
5237
  "github-actions": GithubActionsReporter
5232
5238
  };
5233
5239
 
5234
- export { BasicReporter as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter as J, Logger as L, ReportersMap as R, TapReporter as T, VerboseReporter as V, DotReporter as a, JUnitReporter as b, TapFlatReporter as c, TestCase as d, TestFile as e, TestSuite as f, BenchmarkReportsMap as g, TestProject as h, generateCodeFrame as i, highlightCode as j, BlobReporter as k, parse as p, readBlobs as r, stringify as s };
5240
+ export { BasicReporter as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JsonReporter as J, Logger as L, ReportersMap as R, TapReporter as T, VerboseReporter as V, DotReporter as a, JUnitReporter as b, TapFlatReporter as c, TestFile as d, TestCase as e, TestModule as f, TestSuite as g, BenchmarkReportsMap as h, TestProject as i, generateCodeFrame as j, BlobReporter as k, parse as p, readBlobs as r, stringify as s };