vitest 3.2.0-beta.3 → 3.2.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.
- package/LICENSE.md +29 -0
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -2
- package/dist/chunks/{base.D4119yLM.js → base.Cg0miDlQ.js} +10 -14
- package/dist/chunks/{benchmark.Cf_PACH1.js → benchmark.CYdenmiT.js} +4 -6
- package/dist/chunks/{cac.DWaWHIIE.js → cac.6rXCxFY1.js} +66 -136
- package/dist/chunks/{cli-api.CnmEXkxs.js → cli-api.Cej3MBjA.js} +1250 -1335
- package/dist/chunks/{console.Cwr-MFPV.js → console.CtFJOzRO.js} +24 -45
- package/dist/chunks/{coverage.0iPg4Wrz.js → coverage.DVF1vEu8.js} +4 -12
- package/dist/chunks/{coverage.C73DaDgS.js → coverage.EIiagJJP.js} +484 -1003
- package/dist/chunks/{creator.C8WKy2eW.js → creator.GK6I-cL4.js} +29 -76
- package/dist/chunks/{date.ByMsSlOr.js → date.Bq6ZW5rf.js} +3 -8
- package/dist/chunks/{defaults.DpVH7vbg.js → defaults.B7q_naMc.js} +1 -1
- package/dist/chunks/{env.Dq0hM4Xv.js → env.D4Lgay0q.js} +1 -1
- package/dist/chunks/{execute.B3q-2LPV.js → execute.B7h3T_Hc.js} +104 -220
- package/dist/chunks/{git.DXfdBEfR.js → git.BVQ8w_Sw.js} +1 -3
- package/dist/chunks/{global.d.BNLIi6yo.d.ts → global.d.MAmajcmJ.d.ts} +2 -0
- package/dist/chunks/{globals.CI21aWXF.js → globals.DEHgCU4V.js} +5 -5
- package/dist/chunks/{index.Bter3jj9.js → index.BZ0g1JD2.js} +366 -628
- package/dist/chunks/{index.CbT4iuwc.js → index.BbB8_kAK.js} +22 -24
- package/dist/chunks/{index.JOzufsrU.js → index.CIyJn3t1.js} +37 -82
- package/dist/chunks/{index.DNgLEKsQ.js → index.CdQS2e2Q.js} +2 -2
- package/dist/chunks/{index.2jgTs_Q5.js → index.CmSc2RE5.js} +69 -107
- package/dist/chunks/{inspector.BFsh5KO0.js → inspector.C914Efll.js} +1 -1
- package/dist/chunks/{node.Be-ntJnD.js → node.fjCdwEIl.js} +1 -1
- package/dist/chunks/{reporters.d.Bt4IGtsa.d.ts → reporters.d.C1ogPriE.d.ts} +24 -4
- package/dist/chunks/{rpc.BKExFSRG.js → rpc.Iovn4oWe.js} +9 -19
- package/dist/chunks/{runBaseTests.B_M1TTsK.js → runBaseTests.Dd85QTll.js} +18 -31
- package/dist/chunks/{setup-common.CF-O-dZX.js → setup-common.Dd054P77.js} +15 -42
- package/dist/chunks/{typechecker.BgzF-6iO.js → typechecker.DRKU1-1g.js} +106 -186
- package/dist/chunks/{utils.DPCq3gzW.js → utils.CAioKnHs.js} +6 -14
- package/dist/chunks/{utils.BlI4TC7Y.js → utils.XdZDrNZV.js} +5 -13
- package/dist/chunks/{vi.pkoYCV6A.js → vi.bdSIJ99Y.js} +118 -267
- package/dist/chunks/{vite.d.B-Kx3KCF.d.ts → vite.d.DqE4-hhK.d.ts} +1 -1
- package/dist/chunks/{vm.DPYem2so.js → vm.BThCzidc.js} +98 -214
- package/dist/chunks/{worker.d.Bl1O4kuf.d.ts → worker.d.DvqK5Vmu.d.ts} +1 -1
- package/dist/chunks/{worker.d.BKbBp2ga.d.ts → worker.d.tQu2eJQy.d.ts} +3 -1
- package/dist/cli.js +4 -4
- package/dist/config.cjs +1 -1
- package/dist/config.d.ts +4 -4
- package/dist/config.js +2 -2
- package/dist/coverage.d.ts +2 -2
- package/dist/coverage.js +5 -5
- package/dist/environments.js +1 -1
- package/dist/execute.d.ts +1 -1
- package/dist/execute.js +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.js +5 -5
- package/dist/node.d.ts +7 -7
- package/dist/node.js +12 -14
- package/dist/reporters.d.ts +2 -2
- package/dist/reporters.js +4 -4
- package/dist/runners.d.ts +5 -2
- package/dist/runners.js +51 -80
- package/dist/snapshot.js +2 -2
- package/dist/suite.js +2 -2
- package/dist/worker.js +36 -42
- package/dist/workers/forks.js +4 -4
- package/dist/workers/runVmTests.js +15 -21
- package/dist/workers/threads.js +4 -4
- package/dist/workers/vmForks.js +6 -6
- package/dist/workers/vmThreads.js +6 -6
- package/dist/workers.d.ts +2 -2
- package/dist/workers.js +10 -10
- package/package.json +16 -14
|
@@ -4,8 +4,8 @@ import { slash, toArray, isPrimitive, inspect, positionToOffset, lineSplitRE } f
|
|
|
4
4
|
import { parseStacktrace, parseErrorStacktrace } from '@vitest/utils/source-map';
|
|
5
5
|
import { isAbsolute, relative, dirname, basename, resolve, normalize } from 'pathe';
|
|
6
6
|
import c from 'tinyrainbow';
|
|
7
|
-
import { i as isTTY } from './env.
|
|
8
|
-
import { h as hasFailedSnapshot, g as getOutputFile, T as TypeCheckError } from './typechecker.
|
|
7
|
+
import { i as isTTY } from './env.D4Lgay0q.js';
|
|
8
|
+
import { h as hasFailedSnapshot, g as getOutputFile, T as TypeCheckError } from './typechecker.DRKU1-1g.js';
|
|
9
9
|
import { stripVTControlCharacters } from 'node:util';
|
|
10
10
|
import { existsSync, readFileSync, promises } from 'node:fs';
|
|
11
11
|
import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
|
|
@@ -40,9 +40,7 @@ const labelDefaultColors = [
|
|
|
40
40
|
];
|
|
41
41
|
function getCols(delta = 0) {
|
|
42
42
|
let length = process.stdout?.columns;
|
|
43
|
-
if (!length || Number.isNaN(length))
|
|
44
|
-
length = 30;
|
|
45
|
-
}
|
|
43
|
+
if (!length || Number.isNaN(length)) length = 30;
|
|
46
44
|
return Math.max(length + delta, 0);
|
|
47
45
|
}
|
|
48
46
|
function errorBanner(message) {
|
|
@@ -53,9 +51,8 @@ function divider(text, left, right, color) {
|
|
|
53
51
|
const c = color || ((text) => text);
|
|
54
52
|
if (text) {
|
|
55
53
|
const textLength = stripVTControlCharacters(text).length;
|
|
56
|
-
if (left == null && right != null)
|
|
57
|
-
|
|
58
|
-
} else {
|
|
54
|
+
if (left == null && right != null) left = cols - textLength - right;
|
|
55
|
+
else {
|
|
59
56
|
left = left ?? Math.floor((cols - textLength) / 2);
|
|
60
57
|
right = cols - textLength - left;
|
|
61
58
|
}
|
|
@@ -66,9 +63,7 @@ function divider(text, left, right, color) {
|
|
|
66
63
|
return F_LONG_DASH.repeat(cols);
|
|
67
64
|
}
|
|
68
65
|
function formatTestPath(root, path) {
|
|
69
|
-
if (isAbsolute(path))
|
|
70
|
-
path = relative(root, path);
|
|
71
|
-
}
|
|
66
|
+
if (isAbsolute(path)) path = relative(root, path);
|
|
72
67
|
const dir = dirname(path);
|
|
73
68
|
const ext = path.match(/(\.(spec|test)\.[cm]?[tj]sx?)$/)?.[0] || "";
|
|
74
69
|
const base = basename(path, ext);
|
|
@@ -76,22 +71,11 @@ function formatTestPath(root, path) {
|
|
|
76
71
|
}
|
|
77
72
|
function renderSnapshotSummary(rootDir, snapshots) {
|
|
78
73
|
const summary = [];
|
|
79
|
-
if (snapshots.added) {
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
if (snapshots.
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
if (snapshots.updated) {
|
|
86
|
-
summary.push(c.bold(c.green(`${snapshots.updated} updated `)));
|
|
87
|
-
}
|
|
88
|
-
if (snapshots.filesRemoved) {
|
|
89
|
-
if (snapshots.didUpdate) {
|
|
90
|
-
summary.push(c.bold(c.green(`${snapshots.filesRemoved} files removed `)));
|
|
91
|
-
} else {
|
|
92
|
-
summary.push(c.bold(c.yellow(`${snapshots.filesRemoved} files obsolete `)));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
74
|
+
if (snapshots.added) summary.push(c.bold(c.green(`${snapshots.added} written`)));
|
|
75
|
+
if (snapshots.unmatched) summary.push(c.bold(c.red(`${snapshots.unmatched} failed`)));
|
|
76
|
+
if (snapshots.updated) summary.push(c.bold(c.green(`${snapshots.updated} updated `)));
|
|
77
|
+
if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.filesRemoved} files removed `)));
|
|
78
|
+
else summary.push(c.bold(c.yellow(`${snapshots.filesRemoved} files obsolete `)));
|
|
95
79
|
if (snapshots.filesRemovedList && snapshots.filesRemovedList.length) {
|
|
96
80
|
const [head, ...tail] = snapshots.filesRemovedList;
|
|
97
81
|
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, head)}`);
|
|
@@ -100,11 +84,8 @@ function renderSnapshotSummary(rootDir, snapshots) {
|
|
|
100
84
|
});
|
|
101
85
|
}
|
|
102
86
|
if (snapshots.unchecked) {
|
|
103
|
-
if (snapshots.didUpdate) {
|
|
104
|
-
|
|
105
|
-
} else {
|
|
106
|
-
summary.push(c.bold(c.yellow(`${snapshots.unchecked} obsolete`)));
|
|
107
|
-
}
|
|
87
|
+
if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.unchecked} removed`)));
|
|
88
|
+
else summary.push(c.bold(c.yellow(`${snapshots.unchecked} obsolete`)));
|
|
108
89
|
snapshots.uncheckedKeysByFile.forEach((uncheckedFile) => {
|
|
109
90
|
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
|
|
110
91
|
uncheckedFile.keys.forEach((key) => summary.push(` ${c.gray(F_DOT)} ${key}`));
|
|
@@ -116,9 +97,7 @@ function countTestErrors(tasks) {
|
|
|
116
97
|
return tasks.reduce((c, i) => c + (i.result?.errors?.length || 0), 0);
|
|
117
98
|
}
|
|
118
99
|
function getStateString$1(tasks, name = "tests", showTotal = true) {
|
|
119
|
-
if (tasks.length === 0) {
|
|
120
|
-
return c.dim(`no ${name}`);
|
|
121
|
-
}
|
|
100
|
+
if (tasks.length === 0) return c.dim(`no ${name}`);
|
|
122
101
|
const passed = tasks.filter((i) => i.result?.state === "pass");
|
|
123
102
|
const failed = tasks.filter((i) => i.result?.state === "fail");
|
|
124
103
|
const skipped = tasks.filter((i) => i.mode === "skip");
|
|
@@ -131,41 +110,25 @@ function getStateString$1(tasks, name = "tests", showTotal = true) {
|
|
|
131
110
|
].filter(Boolean).join(c.dim(" | ")) + (showTotal ? c.gray(` (${tasks.length})`) : "");
|
|
132
111
|
}
|
|
133
112
|
function getStateSymbol(task) {
|
|
134
|
-
if (task.mode === "skip" || task.mode === "todo")
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
if (!task.result) {
|
|
138
|
-
return pending$1;
|
|
139
|
-
}
|
|
113
|
+
if (task.mode === "skip" || task.mode === "todo") return skipped;
|
|
114
|
+
if (!task.result) return pending$1;
|
|
140
115
|
if (task.result.state === "run" || task.result.state === "queued") {
|
|
141
|
-
if (task.type === "suite")
|
|
142
|
-
return pointer;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (task.result.state === "pass") {
|
|
146
|
-
return task.meta?.benchmark ? benchmarkPass : testPass;
|
|
147
|
-
}
|
|
148
|
-
if (task.result.state === "fail") {
|
|
149
|
-
return task.type === "suite" ? suiteFail : taskFail;
|
|
116
|
+
if (task.type === "suite") return pointer;
|
|
150
117
|
}
|
|
118
|
+
if (task.result.state === "pass") return task.meta?.benchmark ? benchmarkPass : testPass;
|
|
119
|
+
if (task.result.state === "fail") return task.type === "suite" ? suiteFail : taskFail;
|
|
151
120
|
return " ";
|
|
152
121
|
}
|
|
153
122
|
function formatTimeString(date) {
|
|
154
123
|
return date.toTimeString().split(" ")[0];
|
|
155
124
|
}
|
|
156
125
|
function formatTime(time) {
|
|
157
|
-
if (time > 1e3) {
|
|
158
|
-
return `${(time / 1e3).toFixed(2)}s`;
|
|
159
|
-
}
|
|
126
|
+
if (time > 1e3) return `${(time / 1e3).toFixed(2)}s`;
|
|
160
127
|
return `${Math.round(time)}ms`;
|
|
161
128
|
}
|
|
162
129
|
function formatProjectName(project, suffix = " ") {
|
|
163
|
-
if (!project?.name)
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
if (!c.isColorSupported) {
|
|
167
|
-
return `|${project.name}|${suffix}`;
|
|
168
|
-
}
|
|
130
|
+
if (!project?.name) return "";
|
|
131
|
+
if (!c.isColorSupported) return `|${project.name}|${suffix}`;
|
|
169
132
|
let background = project.color && c[`bg${capitalize(project.color)}`];
|
|
170
133
|
if (!background) {
|
|
171
134
|
const index = project.name.split("").reduce((acc, v, idx) => acc + v.charCodeAt(0) + idx, 0);
|
|
@@ -182,9 +145,7 @@ function padSummaryTitle(str) {
|
|
|
182
145
|
}
|
|
183
146
|
function truncateString(text, maxLength) {
|
|
184
147
|
const plainText = stripVTControlCharacters(text);
|
|
185
|
-
if (plainText.length <= maxLength)
|
|
186
|
-
return text;
|
|
187
|
-
}
|
|
148
|
+
if (plainText.length <= maxLength) return text;
|
|
188
149
|
return `${plainText.slice(0, maxLength - 1)}…`;
|
|
189
150
|
}
|
|
190
151
|
function capitalize(text) {
|
|
@@ -222,11 +183,11 @@ class BaseReporter {
|
|
|
222
183
|
watchFilters;
|
|
223
184
|
failedUnwatchedFiles = [];
|
|
224
185
|
isTTY;
|
|
225
|
-
ctx =
|
|
186
|
+
ctx = void 0;
|
|
226
187
|
renderSucceed = false;
|
|
227
188
|
verbose = false;
|
|
228
|
-
_filesInWatchMode = new Map();
|
|
229
|
-
_timeStart = formatTimeString(new Date());
|
|
189
|
+
_filesInWatchMode = /* @__PURE__ */ new Map();
|
|
190
|
+
_timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
230
191
|
constructor(options = {}) {
|
|
231
192
|
this.isTTY = options.isTTY ?? isTTY;
|
|
232
193
|
}
|
|
@@ -246,40 +207,25 @@ class BaseReporter {
|
|
|
246
207
|
}
|
|
247
208
|
onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
|
248
209
|
this.end = performance$1.now();
|
|
249
|
-
if (!files.length && !errors.length)
|
|
250
|
-
|
|
251
|
-
} else {
|
|
252
|
-
this.reportSummary(files, errors);
|
|
253
|
-
}
|
|
210
|
+
if (!files.length && !errors.length) this.ctx.logger.printNoTestFound(this.ctx.filenamePattern);
|
|
211
|
+
else this.reportSummary(files, errors);
|
|
254
212
|
}
|
|
255
213
|
onTestCaseResult(testCase) {
|
|
256
|
-
if (testCase.result().state === "failed")
|
|
257
|
-
this.logFailedTask(testCase.task);
|
|
258
|
-
}
|
|
214
|
+
if (testCase.result().state === "failed") this.logFailedTask(testCase.task);
|
|
259
215
|
}
|
|
260
216
|
onTestSuiteResult(testSuite) {
|
|
261
|
-
if (testSuite.state() === "failed")
|
|
262
|
-
this.logFailedTask(testSuite.task);
|
|
263
|
-
}
|
|
217
|
+
if (testSuite.state() === "failed") this.logFailedTask(testSuite.task);
|
|
264
218
|
}
|
|
265
219
|
onTestModuleEnd(testModule) {
|
|
266
|
-
if (testModule.state() === "failed")
|
|
267
|
-
this.logFailedTask(testModule.task);
|
|
268
|
-
}
|
|
220
|
+
if (testModule.state() === "failed") this.logFailedTask(testModule.task);
|
|
269
221
|
this.printTestModule(testModule);
|
|
270
222
|
}
|
|
271
223
|
logFailedTask(task) {
|
|
272
|
-
if (this.ctx.config.silent === "passed-only")
|
|
273
|
-
for (const log of task.logs || []) {
|
|
274
|
-
this.onUserConsoleLog(log, "failed");
|
|
275
|
-
}
|
|
276
|
-
}
|
|
224
|
+
if (this.ctx.config.silent === "passed-only") for (const log of task.logs || []) this.onUserConsoleLog(log, "failed");
|
|
277
225
|
}
|
|
278
226
|
printTestModule(testModule) {
|
|
279
227
|
const moduleState = testModule.state();
|
|
280
|
-
if (moduleState === "queued" || moduleState === "pending")
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
228
|
+
if (moduleState === "queued" || moduleState === "pending") return;
|
|
283
229
|
let testsCount = 0;
|
|
284
230
|
let failedCount = 0;
|
|
285
231
|
let skippedCount = 0;
|
|
@@ -289,28 +235,20 @@ class BaseReporter {
|
|
|
289
235
|
const originalLog = this.log.bind(this);
|
|
290
236
|
this.log = (msg) => logs.push(msg);
|
|
291
237
|
const visit = (suiteState, children) => {
|
|
292
|
-
for (const child of children) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
skippedCount++;
|
|
307
|
-
}
|
|
308
|
-
if (this.ctx.config.hideSkippedTests && suiteState === "skipped") {
|
|
309
|
-
// Skipped suites are hidden when --hideSkippedTests
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
this.printTestCase(moduleState, child);
|
|
313
|
-
}
|
|
238
|
+
for (const child of children) if (child.type === "suite") {
|
|
239
|
+
const suiteState = child.state();
|
|
240
|
+
// Skipped suites are hidden when --hideSkippedTests, print otherwise
|
|
241
|
+
if (!this.ctx.config.hideSkippedTests || suiteState !== "skipped") this.printTestSuite(child);
|
|
242
|
+
visit(suiteState, child.children);
|
|
243
|
+
} else {
|
|
244
|
+
const testResult = child.result();
|
|
245
|
+
testsCount++;
|
|
246
|
+
if (testResult.state === "failed") failedCount++;
|
|
247
|
+
else if (testResult.state === "skipped") skippedCount++;
|
|
248
|
+
if (this.ctx.config.hideSkippedTests && suiteState === "skipped")
|
|
249
|
+
// Skipped suites are hidden when --hideSkippedTests
|
|
250
|
+
continue;
|
|
251
|
+
this.printTestCase(moduleState, child);
|
|
314
252
|
}
|
|
315
253
|
};
|
|
316
254
|
try {
|
|
@@ -330,49 +268,29 @@ class BaseReporter {
|
|
|
330
268
|
const { duration, retryCount, repeatCount } = test.diagnostic() || {};
|
|
331
269
|
const padding = this.getTestIndentation(test.task);
|
|
332
270
|
let suffix = this.getDurationPrefix(test.task);
|
|
333
|
-
if (retryCount != null && retryCount > 0) {
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
if (repeatCount != null && repeatCount > 0) {
|
|
337
|
-
suffix += c.yellow(` (repeat x${repeatCount})`);
|
|
338
|
-
}
|
|
271
|
+
if (retryCount != null && retryCount > 0) suffix += c.yellow(` (retry x${retryCount})`);
|
|
272
|
+
if (repeatCount != null && repeatCount > 0) suffix += c.yellow(` (repeat x${repeatCount})`);
|
|
339
273
|
if (testResult.state === "failed") {
|
|
340
274
|
this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, c.dim(" > "))}`) + suffix);
|
|
341
275
|
// print short errors, full errors will be at the end in summary
|
|
342
276
|
testResult.errors.forEach((error) => {
|
|
343
277
|
const message = this.formatShortError(error);
|
|
344
|
-
if (message) {
|
|
345
|
-
this.log(c.red(` ${padding}${message}`));
|
|
346
|
-
}
|
|
278
|
+
if (message) this.log(c.red(` ${padding}${message}`));
|
|
347
279
|
});
|
|
348
|
-
} else if (duration && duration > this.ctx.config.slowTestThreshold) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
this.log(` ${padding}${getStateSymbol(test.task)} ${this.getTestName(test.task, c.dim(" > "))}${c.dim(c.gray(` [${testResult.note}]`))}`);
|
|
352
|
-
} else if (this.renderSucceed || moduleState === "failed") {
|
|
353
|
-
this.log(` ${padding}${getStateSymbol(test.task)} ${this.getTestName(test.task, c.dim(" > "))}${suffix}`);
|
|
354
|
-
}
|
|
280
|
+
} else if (duration && duration > this.ctx.config.slowTestThreshold) this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, c.dim(" > "))} ${suffix}`);
|
|
281
|
+
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}]`))}`);
|
|
282
|
+
else if (this.renderSucceed || moduleState === "failed") this.log(` ${padding}${getStateSymbol(test.task)} ${this.getTestName(test.task, c.dim(" > "))}${suffix}`);
|
|
355
283
|
}
|
|
356
284
|
getModuleLog(testModule, counts) {
|
|
357
285
|
let state = c.dim(`${counts.tests} test${counts.tests > 1 ? "s" : ""}`);
|
|
358
|
-
if (counts.failed) {
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
if (counts.skipped) {
|
|
362
|
-
state += c.dim(" | ") + c.yellow(`${counts.skipped} skipped`);
|
|
363
|
-
}
|
|
286
|
+
if (counts.failed) state += c.dim(" | ") + c.red(`${counts.failed} failed`);
|
|
287
|
+
if (counts.skipped) state += c.dim(" | ") + c.yellow(`${counts.skipped} skipped`);
|
|
364
288
|
let suffix = c.dim("(") + state + c.dim(")") + this.getDurationPrefix(testModule.task);
|
|
365
289
|
const diagnostic = testModule.diagnostic();
|
|
366
|
-
if (diagnostic.heap != null) {
|
|
367
|
-
suffix += c.magenta(` ${Math.floor(diagnostic.heap / 1024 / 1024)} MB heap used`);
|
|
368
|
-
}
|
|
290
|
+
if (diagnostic.heap != null) suffix += c.magenta(` ${Math.floor(diagnostic.heap / 1024 / 1024)} MB heap used`);
|
|
369
291
|
let title = getStateSymbol(testModule.task);
|
|
370
|
-
if (testModule.meta().typecheck) {
|
|
371
|
-
|
|
372
|
-
}
|
|
373
|
-
if (testModule.project.name) {
|
|
374
|
-
title += ` ${formatProjectName(testModule.project, "")}`;
|
|
375
|
-
}
|
|
292
|
+
if (testModule.meta().typecheck) title += ` ${c.bgBlue(c.bold(" TS "))}`;
|
|
293
|
+
if (testModule.project.name) title += ` ${formatProjectName(testModule.project, "")}`;
|
|
376
294
|
return ` ${title} ${testModule.task.name} ${suffix}`;
|
|
377
295
|
}
|
|
378
296
|
printTestSuite(_suite) {
|
|
@@ -387,28 +305,31 @@ class BaseReporter {
|
|
|
387
305
|
getTestIndentation(_test) {
|
|
388
306
|
return " ";
|
|
389
307
|
}
|
|
308
|
+
printAnnotations(test, console, padding = 0) {
|
|
309
|
+
const annotations = test.annotations();
|
|
310
|
+
if (!annotations.length) return;
|
|
311
|
+
const PADDING = " ".repeat(padding);
|
|
312
|
+
annotations.forEach(({ location, type, message }) => {
|
|
313
|
+
if (location) {
|
|
314
|
+
const file = relative(test.project.config.root, location.file);
|
|
315
|
+
this[console](`${PADDING}${c.blue(F_POINTER)} ${c.gray(`${file}:${location.line}:${location.column}`)} ${c.bold(type)}`);
|
|
316
|
+
} else this[console](`${PADDING}${c.blue(F_POINTER)} ${c.bold(type)}`);
|
|
317
|
+
this[console](`${PADDING} ${c.blue(F_DOWN_RIGHT)} ${message}`);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
390
320
|
getDurationPrefix(task) {
|
|
391
|
-
if (!task.result?.duration)
|
|
392
|
-
return "";
|
|
393
|
-
}
|
|
321
|
+
if (!task.result?.duration) return "";
|
|
394
322
|
const color = task.result.duration > this.ctx.config.slowTestThreshold ? c.yellow : c.green;
|
|
395
323
|
return color(` ${Math.round(task.result.duration)}${c.dim("ms")}`);
|
|
396
324
|
}
|
|
397
325
|
onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
|
398
326
|
const failed = errors.length > 0 || hasFailed(files);
|
|
399
|
-
if (failed)
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
this.log(withLabel("red", "CANCELLED", "Test run cancelled. Watching for file changes..."));
|
|
403
|
-
} else {
|
|
404
|
-
this.log(withLabel("green", "PASS", "Waiting for file changes..."));
|
|
405
|
-
}
|
|
327
|
+
if (failed) this.log(withLabel("red", "FAIL", "Tests failed. Watching for file changes..."));
|
|
328
|
+
else if (this.ctx.isCancelling) this.log(withLabel("red", "CANCELLED", "Test run cancelled. Watching for file changes..."));
|
|
329
|
+
else this.log(withLabel("green", "PASS", "Waiting for file changes..."));
|
|
406
330
|
const hints = [c.dim("press ") + c.bold("h") + c.dim(" to show help")];
|
|
407
|
-
if (hasFailedSnapshot(files))
|
|
408
|
-
|
|
409
|
-
} else {
|
|
410
|
-
hints.push(c.dim("press ") + c.bold("q") + c.dim(" to quit"));
|
|
411
|
-
}
|
|
331
|
+
if (hasFailedSnapshot(files)) hints.unshift(c.dim("press ") + c.bold(c.yellow("u")) + c.dim(" to update snapshot"));
|
|
332
|
+
else hints.push(c.dim("press ") + c.bold("q") + c.dim(" to quit"));
|
|
412
333
|
this.log(BADGE_PADDING + hints.join(c.dim(", ")));
|
|
413
334
|
}
|
|
414
335
|
onWatcherRerun(files, trigger) {
|
|
@@ -426,41 +347,26 @@ class BaseReporter {
|
|
|
426
347
|
}
|
|
427
348
|
this.ctx.logger.clearFullScreen();
|
|
428
349
|
this.log(withLabel("blue", "RERUN", banner));
|
|
429
|
-
if (this.ctx.configOverride.project)
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if (this.ctx.filenamePattern) {
|
|
433
|
-
this.log(BADGE_PADDING + c.dim(" Filename pattern: ") + c.blue(this.ctx.filenamePattern.join(", ")));
|
|
434
|
-
}
|
|
435
|
-
if (this.ctx.configOverride.testNamePattern) {
|
|
436
|
-
this.log(BADGE_PADDING + c.dim(" Test name pattern: ") + c.blue(String(this.ctx.configOverride.testNamePattern)));
|
|
437
|
-
}
|
|
350
|
+
if (this.ctx.configOverride.project) this.log(BADGE_PADDING + c.dim(" Project name: ") + c.blue(toArray(this.ctx.configOverride.project).join(", ")));
|
|
351
|
+
if (this.ctx.filenamePattern) this.log(BADGE_PADDING + c.dim(" Filename pattern: ") + c.blue(this.ctx.filenamePattern.join(", ")));
|
|
352
|
+
if (this.ctx.configOverride.testNamePattern) this.log(BADGE_PADDING + c.dim(" Test name pattern: ") + c.blue(String(this.ctx.configOverride.testNamePattern)));
|
|
438
353
|
this.log("");
|
|
439
|
-
for (const testModule of this.failedUnwatchedFiles)
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
this._timeStart = formatTimeString(new Date());
|
|
354
|
+
for (const testModule of this.failedUnwatchedFiles) this.printTestModule(testModule);
|
|
355
|
+
this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
443
356
|
this.start = performance$1.now();
|
|
444
357
|
}
|
|
445
358
|
onUserConsoleLog(log, taskState) {
|
|
446
|
-
if (!this.shouldLog(log, taskState))
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
359
|
+
if (!this.shouldLog(log, taskState)) return;
|
|
449
360
|
const output = log.type === "stdout" ? this.ctx.logger.outputStream : this.ctx.logger.errorStream;
|
|
450
361
|
const write = (msg) => output.write(msg);
|
|
451
362
|
let headerText = "unknown test";
|
|
452
|
-
const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) :
|
|
453
|
-
if (task)
|
|
454
|
-
|
|
455
|
-
} else if (log.taskId && log.taskId !== "__vitest__unknown_test__") {
|
|
456
|
-
headerText = log.taskId;
|
|
457
|
-
}
|
|
363
|
+
const task = log.taskId ? this.ctx.state.idMap.get(log.taskId) : void 0;
|
|
364
|
+
if (task) headerText = getFullName(task, c.dim(" > "));
|
|
365
|
+
else if (log.taskId && log.taskId !== "__vitest__unknown_test__") headerText = log.taskId;
|
|
458
366
|
write(c.gray(log.type + c.dim(` | ${headerText}\n`)) + log.content);
|
|
459
367
|
if (log.origin) {
|
|
460
368
|
// browser logs don't have an extra end of line at the end like Node.js does
|
|
461
|
-
if (log.browser)
|
|
462
|
-
write("\n");
|
|
463
|
-
}
|
|
369
|
+
if (log.browser) write("\n");
|
|
464
370
|
const project = task ? this.ctx.getProjectByName(task.file.projectName || "") : this.ctx.getRootProject();
|
|
465
371
|
const stack = log.browser ? project.browser?.parseStacktrace(log.origin) || [] : parseStacktrace(log.origin);
|
|
466
372
|
const highlight = task && stack.find((i) => i.file === task.file.filepath);
|
|
@@ -477,16 +383,10 @@ class BaseReporter {
|
|
|
477
383
|
this.log(c.yellow("Test removed...") + (trigger ? c.dim(` [ ${this.relative(trigger)} ]\n`) : ""));
|
|
478
384
|
}
|
|
479
385
|
shouldLog(log, taskState) {
|
|
480
|
-
if (this.ctx.config.silent === true)
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
if (this.ctx.config.silent === "passed-only" && taskState !== "failed") {
|
|
484
|
-
return false;
|
|
485
|
-
}
|
|
386
|
+
if (this.ctx.config.silent === true) return false;
|
|
387
|
+
if (this.ctx.config.silent === "passed-only" && taskState !== "failed") return false;
|
|
486
388
|
const shouldLog = this.ctx.config.onConsoleLog?.(log.content, log.type);
|
|
487
|
-
if (shouldLog === false)
|
|
488
|
-
return shouldLog;
|
|
489
|
-
}
|
|
389
|
+
if (shouldLog === false) return shouldLog;
|
|
490
390
|
return true;
|
|
491
391
|
}
|
|
492
392
|
onServerRestart(reason) {
|
|
@@ -494,11 +394,8 @@ class BaseReporter {
|
|
|
494
394
|
}
|
|
495
395
|
reportSummary(files, errors) {
|
|
496
396
|
this.printErrorsSummary(files, errors);
|
|
497
|
-
if (this.ctx.config.mode === "benchmark")
|
|
498
|
-
|
|
499
|
-
} else {
|
|
500
|
-
this.reportTestSummary(files, errors);
|
|
501
|
-
}
|
|
397
|
+
if (this.ctx.config.mode === "benchmark") this.reportBenchmarkSummary(files);
|
|
398
|
+
else this.reportTestSummary(files, errors);
|
|
502
399
|
}
|
|
503
400
|
reportTestSummary(files, errors) {
|
|
504
401
|
this.log();
|
|
@@ -509,25 +406,20 @@ class BaseReporter {
|
|
|
509
406
|
const title = index === 0 ? "Snapshots" : "";
|
|
510
407
|
this.log(`${padSummaryTitle(title)} ${snapshot}`);
|
|
511
408
|
}
|
|
512
|
-
if (snapshotOutput.length > 1)
|
|
513
|
-
this.log();
|
|
514
|
-
}
|
|
409
|
+
if (snapshotOutput.length > 1) this.log();
|
|
515
410
|
this.log(padSummaryTitle("Test Files"), getStateString$1(affectedFiles));
|
|
516
411
|
this.log(padSummaryTitle("Tests"), getStateString$1(tests));
|
|
517
412
|
if (this.ctx.projects.some((c) => c.config.typecheck.enabled)) {
|
|
518
413
|
const failed = tests.filter((t) => t.meta?.typecheck && t.result?.errors?.length);
|
|
519
414
|
this.log(padSummaryTitle("Type Errors"), failed.length ? c.bold(c.red(`${failed.length} failed`)) : c.dim("no errors"));
|
|
520
415
|
}
|
|
521
|
-
if (errors.length) {
|
|
522
|
-
this.log(padSummaryTitle("Errors"), c.bold(c.red(`${errors.length} error${errors.length > 1 ? "s" : ""}`)));
|
|
523
|
-
}
|
|
416
|
+
if (errors.length) this.log(padSummaryTitle("Errors"), c.bold(c.red(`${errors.length} error${errors.length > 1 ? "s" : ""}`)));
|
|
524
417
|
this.log(padSummaryTitle("Start at"), this._timeStart);
|
|
525
418
|
const collectTime = sum(files, (file) => file.collectDuration);
|
|
526
419
|
const testsTime = sum(files, (file) => file.result?.duration);
|
|
527
420
|
const setupTime = sum(files, (file) => file.setupDuration);
|
|
528
|
-
if (this.watchFilters)
|
|
529
|
-
|
|
530
|
-
} else {
|
|
421
|
+
if (this.watchFilters) this.log(padSummaryTitle("Duration"), formatTime(collectTime + testsTime + setupTime));
|
|
422
|
+
else {
|
|
531
423
|
const blobs = this.ctx.state.blobs;
|
|
532
424
|
// Execution time is either sum of all runs of `--merge-reports` or the current run's time
|
|
533
425
|
const executionTime = blobs?.executionTimes ? sum(blobs.executionTimes, (time) => time) : this.end - this.start;
|
|
@@ -545,9 +437,7 @@ class BaseReporter {
|
|
|
545
437
|
typecheck && `typecheck ${formatTime(typecheck)}`
|
|
546
438
|
].filter(Boolean).join(", ");
|
|
547
439
|
this.log(padSummaryTitle("Duration"), formatTime(executionTime) + c.dim(` (${timers})`));
|
|
548
|
-
if (blobs?.executionTimes) {
|
|
549
|
-
this.log(padSummaryTitle("Per blob") + blobs.executionTimes.map((time) => ` ${formatTime(time)}`).join(""));
|
|
550
|
-
}
|
|
440
|
+
if (blobs?.executionTimes) this.log(padSummaryTitle("Per blob") + blobs.executionTimes.map((time) => ` ${formatTime(time)}`).join(""));
|
|
551
441
|
}
|
|
552
442
|
this.log();
|
|
553
443
|
}
|
|
@@ -558,7 +448,7 @@ class BaseReporter {
|
|
|
558
448
|
const failedTests = tests.filter((i) => i.result?.state === "fail");
|
|
559
449
|
const failedTotal = countTestErrors(failedSuites) + countTestErrors(failedTests);
|
|
560
450
|
let current = 1;
|
|
561
|
-
const errorDivider = () => this.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`,
|
|
451
|
+
const errorDivider = () => this.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1)))}\n`);
|
|
562
452
|
if (failedSuites.length) {
|
|
563
453
|
this.error(`\n${errorBanner(`Failed Suites ${failedSuites.length}`)}\n`);
|
|
564
454
|
this.printTaskErrors(failedSuites, errorDivider);
|
|
@@ -578,9 +468,7 @@ class BaseReporter {
|
|
|
578
468
|
this.log(`\n${withLabel("cyan", "BENCH", "Summary\n")}`);
|
|
579
469
|
for (const bench of topBenches) {
|
|
580
470
|
const group = bench.suite || bench.file;
|
|
581
|
-
if (!group)
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
471
|
+
if (!group) continue;
|
|
584
472
|
const groupName = getFullName(group, c.dim(" > "));
|
|
585
473
|
const project = this.ctx.projects.find((p) => p.name === bench.file.projectName);
|
|
586
474
|
this.log(` ${formatProjectName(project)}${bench.name}${c.dim(` - ${groupName}`)}`);
|
|
@@ -594,36 +482,28 @@ class BaseReporter {
|
|
|
594
482
|
}
|
|
595
483
|
printTaskErrors(tasks, errorDivider) {
|
|
596
484
|
const errorsQueue = [];
|
|
597
|
-
for (const task of tasks)
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
return projectName === currentProjectName;
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
if (previous) {
|
|
612
|
-
previous[1].push(task);
|
|
613
|
-
} else {
|
|
614
|
-
errorsQueue.push([error, [task]]);
|
|
615
|
-
}
|
|
485
|
+
for (const task of tasks)
|
|
486
|
+
// Merge identical errors
|
|
487
|
+
task.result?.errors?.forEach((error) => {
|
|
488
|
+
let previous;
|
|
489
|
+
if (error?.stack) previous = errorsQueue.find((i) => {
|
|
490
|
+
if (i[0]?.stack !== error.stack) return false;
|
|
491
|
+
const currentProjectName = task?.projectName || task.file?.projectName || "";
|
|
492
|
+
const projectName = i[1][0]?.projectName || i[1][0].file?.projectName || "";
|
|
493
|
+
const currentAnnotations = task.type === "test" && task.annotations;
|
|
494
|
+
const itemAnnotations = i[1][0].type === "test" && i[1][0].annotations;
|
|
495
|
+
return projectName === currentProjectName && deepEqual(currentAnnotations, itemAnnotations);
|
|
616
496
|
});
|
|
617
|
-
|
|
497
|
+
if (previous) previous[1].push(task);
|
|
498
|
+
else errorsQueue.push([error, [task]]);
|
|
499
|
+
});
|
|
618
500
|
for (const [error, tasks] of errorsQueue) {
|
|
619
501
|
for (const task of tasks) {
|
|
620
502
|
const filepath = task?.filepath || "";
|
|
621
503
|
const projectName = task?.projectName || task.file?.projectName || "";
|
|
622
504
|
const project = this.ctx.projects.find((p) => p.name === projectName);
|
|
623
505
|
let name = getFullName(task, c.dim(" > "));
|
|
624
|
-
if (filepath) {
|
|
625
|
-
name += c.dim(` [ ${this.relative(filepath)} ]`);
|
|
626
|
-
}
|
|
506
|
+
if (filepath) name += c.dim(` [ ${this.relative(filepath)} ]`);
|
|
627
507
|
this.ctx.logger.error(`${c.bgRed(c.bold(" FAIL "))} ${formatProjectName(project)}${name}`);
|
|
628
508
|
}
|
|
629
509
|
const screenshotPaths = tasks.map((t) => t.meta?.failScreenshotPath).filter((screenshot) => screenshot != null);
|
|
@@ -633,10 +513,24 @@ class BaseReporter {
|
|
|
633
513
|
screenshotPaths,
|
|
634
514
|
task: tasks[0]
|
|
635
515
|
});
|
|
516
|
+
if (tasks[0].type === "test" && tasks[0].annotations.length) {
|
|
517
|
+
const test = this.ctx.state.getReportedEntity(tasks[0]);
|
|
518
|
+
this.printAnnotations(test, "error", 1);
|
|
519
|
+
this.error();
|
|
520
|
+
}
|
|
636
521
|
errorDivider();
|
|
637
522
|
}
|
|
638
523
|
}
|
|
639
524
|
}
|
|
525
|
+
function deepEqual(a, b) {
|
|
526
|
+
if (a === b) return true;
|
|
527
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
528
|
+
const keysA = Object.keys(a);
|
|
529
|
+
const keysB = Object.keys(b);
|
|
530
|
+
if (keysA.length !== keysB.length) return false;
|
|
531
|
+
for (const key of keysA) if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false;
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
640
534
|
function sum(items, cb) {
|
|
641
535
|
return items.reduce((total, next) => {
|
|
642
536
|
return total + Math.max(cb(next) || 0, 0);
|
|
@@ -650,7 +544,7 @@ class BasicReporter extends BaseReporter {
|
|
|
650
544
|
}
|
|
651
545
|
onInit(ctx) {
|
|
652
546
|
super.onInit(ctx);
|
|
653
|
-
ctx.logger.deprecate(`'basic' reporter is deprecated and will be removed in Vitest v3.\
|
|
547
|
+
ctx.logger.deprecate(`'basic' reporter is deprecated and will be removed in Vitest v3.\nRemove 'basic' from 'reporters' option. To match 'basic' reporter 100%, use configuration:\n${JSON.stringify({ test: { reporters: [["default", { summary: false }]] } }, null, 2)}`);
|
|
654
548
|
}
|
|
655
549
|
reportSummary(files, errors) {
|
|
656
550
|
// non-tty mode doesn't add a new line
|
|
@@ -774,9 +668,7 @@ class BlobReporter {
|
|
|
774
668
|
this.options = options;
|
|
775
669
|
}
|
|
776
670
|
onInit(ctx) {
|
|
777
|
-
if (ctx.config.watch)
|
|
778
|
-
throw new Error("Blob reporter is not supported in watch mode");
|
|
779
|
-
}
|
|
671
|
+
if (ctx.config.watch) throw new Error("Blob reporter is not supported in watch mode");
|
|
780
672
|
this.ctx = ctx;
|
|
781
673
|
this.start = performance.now();
|
|
782
674
|
}
|
|
@@ -789,9 +681,7 @@ class BlobReporter {
|
|
|
789
681
|
}
|
|
790
682
|
const modules = this.ctx.projects.map((project) => {
|
|
791
683
|
return [project.name, [...project.vite.moduleGraph.idToModuleMap.entries()].map((mod) => {
|
|
792
|
-
if (!mod[1].file)
|
|
793
|
-
return null;
|
|
794
|
-
}
|
|
684
|
+
if (!mod[1].file) return null;
|
|
795
685
|
return [
|
|
796
686
|
mod[0],
|
|
797
687
|
mod[1].file,
|
|
@@ -815,9 +705,7 @@ class BlobReporter {
|
|
|
815
705
|
async function writeBlob(content, filename) {
|
|
816
706
|
const report = stringify(content);
|
|
817
707
|
const dir = dirname(filename);
|
|
818
|
-
if (!existsSync(dir)) {
|
|
819
|
-
await mkdir(dir, { recursive: true });
|
|
820
|
-
}
|
|
708
|
+
if (!existsSync(dir)) await mkdir(dir, { recursive: true });
|
|
821
709
|
await writeFile(filename, report, "utf-8");
|
|
822
710
|
}
|
|
823
711
|
async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
|
|
@@ -827,14 +715,10 @@ async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
|
|
|
827
715
|
const promises = blobsFiles.map(async (filename) => {
|
|
828
716
|
const fullPath = resolve(resolvedDir, filename);
|
|
829
717
|
const stats = await stat(fullPath);
|
|
830
|
-
if (!stats.isFile()) {
|
|
831
|
-
throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`);
|
|
832
|
-
}
|
|
718
|
+
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`);
|
|
833
719
|
const content = await readFile(fullPath, "utf-8");
|
|
834
720
|
const [version, files, errors, moduleKeys, coverage, executionTime] = parse(content);
|
|
835
|
-
if (!version) {
|
|
836
|
-
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`);
|
|
837
|
-
}
|
|
721
|
+
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`);
|
|
838
722
|
return {
|
|
839
723
|
version,
|
|
840
724
|
files,
|
|
@@ -846,24 +730,16 @@ async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
|
|
|
846
730
|
};
|
|
847
731
|
});
|
|
848
732
|
const blobs = await Promise.all(promises);
|
|
849
|
-
if (!blobs.length) {
|
|
850
|
-
throw new Error(`vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`);
|
|
851
|
-
}
|
|
733
|
+
if (!blobs.length) throw new Error(`vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`);
|
|
852
734
|
const versions = new Set(blobs.map((blob) => blob.version));
|
|
853
|
-
if (versions.size > 1) {
|
|
854
|
-
|
|
855
|
-
}
|
|
856
|
-
if (!versions.has(currentVersion)) {
|
|
857
|
-
throw new Error(`the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`);
|
|
858
|
-
}
|
|
735
|
+
if (versions.size > 1) throw new Error(`vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received\n\n${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`);
|
|
736
|
+
if (!versions.has(currentVersion)) throw new Error(`the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`);
|
|
859
737
|
// fake module graph - it is used to check if module is imported, but we don't use values inside
|
|
860
738
|
const projects = Object.fromEntries(projectsArray.map((p) => [p.name, p]));
|
|
861
739
|
blobs.forEach((blob) => {
|
|
862
740
|
blob.moduleKeys.forEach(([projectName, moduleIds]) => {
|
|
863
741
|
const project = projects[projectName];
|
|
864
|
-
if (!project)
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
742
|
+
if (!project) return;
|
|
867
743
|
moduleIds.forEach(([moduleId, file, url]) => {
|
|
868
744
|
const moduleNode = project.vite.moduleGraph.createFileOnlyEntry(file);
|
|
869
745
|
moduleNode.url = url;
|
|
@@ -902,7 +778,7 @@ class WindowRenderer {
|
|
|
902
778
|
options;
|
|
903
779
|
streams;
|
|
904
780
|
buffer = [];
|
|
905
|
-
renderInterval =
|
|
781
|
+
renderInterval = void 0;
|
|
906
782
|
renderScheduled = false;
|
|
907
783
|
windowHeight = 0;
|
|
908
784
|
finished = false;
|
|
@@ -954,9 +830,7 @@ class WindowRenderer {
|
|
|
954
830
|
}
|
|
955
831
|
}
|
|
956
832
|
flushBuffer() {
|
|
957
|
-
if (this.buffer.length === 0)
|
|
958
|
-
return this.render();
|
|
959
|
-
}
|
|
833
|
+
if (this.buffer.length === 0) return this.render();
|
|
960
834
|
let current;
|
|
961
835
|
// Concatenate same types into a single render
|
|
962
836
|
for (const next of this.buffer.splice(0)) {
|
|
@@ -971,9 +845,7 @@ class WindowRenderer {
|
|
|
971
845
|
}
|
|
972
846
|
current.message += next.message;
|
|
973
847
|
}
|
|
974
|
-
if (current)
|
|
975
|
-
this.render(current?.message, current?.type);
|
|
976
|
-
}
|
|
848
|
+
if (current) this.render(current?.message, current?.type);
|
|
977
849
|
}
|
|
978
850
|
render(message, type = "output") {
|
|
979
851
|
if (this.finished) {
|
|
@@ -983,45 +855,30 @@ class WindowRenderer {
|
|
|
983
855
|
const windowContent = this.options.getWindow();
|
|
984
856
|
const rowCount = getRenderedRowCount(windowContent, this.options.logger.getColumns());
|
|
985
857
|
let padding = this.windowHeight - rowCount;
|
|
986
|
-
if (padding > 0 && message)
|
|
987
|
-
padding -= getRenderedRowCount([message], this.options.logger.getColumns());
|
|
988
|
-
}
|
|
858
|
+
if (padding > 0 && message) padding -= getRenderedRowCount([message], this.options.logger.getColumns());
|
|
989
859
|
this.write(SYNC_START);
|
|
990
860
|
this.clearWindow();
|
|
991
|
-
if (message)
|
|
992
|
-
|
|
993
|
-
}
|
|
994
|
-
if (padding > 0) {
|
|
995
|
-
this.write("\n".repeat(padding));
|
|
996
|
-
}
|
|
861
|
+
if (message) this.write(message, type);
|
|
862
|
+
if (padding > 0) this.write("\n".repeat(padding));
|
|
997
863
|
this.write(windowContent.join("\n"));
|
|
998
864
|
this.write(SYNC_END);
|
|
999
865
|
this.windowHeight = rowCount + Math.max(0, padding);
|
|
1000
866
|
}
|
|
1001
867
|
clearWindow() {
|
|
1002
|
-
if (this.windowHeight === 0)
|
|
1003
|
-
return;
|
|
1004
|
-
}
|
|
868
|
+
if (this.windowHeight === 0) return;
|
|
1005
869
|
this.write(CLEAR_LINE);
|
|
1006
|
-
for (let i = 1; i < this.windowHeight; i++) {
|
|
1007
|
-
this.write(`${MOVE_CURSOR_ONE_ROW_UP}${CLEAR_LINE}`);
|
|
1008
|
-
}
|
|
870
|
+
for (let i = 1; i < this.windowHeight; i++) this.write(`${MOVE_CURSOR_ONE_ROW_UP}${CLEAR_LINE}`);
|
|
1009
871
|
this.windowHeight = 0;
|
|
1010
872
|
}
|
|
1011
873
|
interceptStream(stream, type) {
|
|
1012
874
|
const original = stream.write;
|
|
1013
875
|
// @ts-expect-error -- not sure how 2 overloads should be typed
|
|
1014
876
|
stream.write = (chunk, _, callback) => {
|
|
1015
|
-
if (chunk)
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
type,
|
|
1021
|
-
message: chunk.toString()
|
|
1022
|
-
});
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
877
|
+
if (chunk) if (this.finished) this.write(chunk.toString(), type);
|
|
878
|
+
else this.buffer.push({
|
|
879
|
+
type,
|
|
880
|
+
message: chunk.toString()
|
|
881
|
+
});
|
|
1025
882
|
callback?.();
|
|
1026
883
|
};
|
|
1027
884
|
return function restore() {
|
|
@@ -1056,13 +913,13 @@ class SummaryReporter {
|
|
|
1056
913
|
tests = emptyCounters();
|
|
1057
914
|
maxParallelTests = 0;
|
|
1058
915
|
/** Currently running test modules, may include finished test modules too */
|
|
1059
|
-
runningModules = new Map();
|
|
916
|
+
runningModules = /* @__PURE__ */ new Map();
|
|
1060
917
|
/** ID of finished `this.runningModules` that are currently being shown */
|
|
1061
|
-
finishedModules = new Map();
|
|
918
|
+
finishedModules = /* @__PURE__ */ new Map();
|
|
1062
919
|
startTime = "";
|
|
1063
920
|
currentTime = 0;
|
|
1064
921
|
duration = 0;
|
|
1065
|
-
durationInterval =
|
|
922
|
+
durationInterval = void 0;
|
|
1066
923
|
onInit(ctx, options = {}) {
|
|
1067
924
|
this.ctx = ctx;
|
|
1068
925
|
this.options = {
|
|
@@ -1116,9 +973,7 @@ class SummaryReporter {
|
|
|
1116
973
|
}
|
|
1117
974
|
onHookStart(options) {
|
|
1118
975
|
const stats = this.getHookStats(options);
|
|
1119
|
-
if (!stats)
|
|
1120
|
-
return;
|
|
1121
|
-
}
|
|
976
|
+
if (!stats) return;
|
|
1122
977
|
const hook = {
|
|
1123
978
|
name: options.name,
|
|
1124
979
|
visible: false,
|
|
@@ -1134,21 +989,15 @@ class SummaryReporter {
|
|
|
1134
989
|
}
|
|
1135
990
|
onHookEnd(options) {
|
|
1136
991
|
const stats = this.getHookStats(options);
|
|
1137
|
-
if (stats?.hook?.name !== options.name)
|
|
1138
|
-
return;
|
|
1139
|
-
}
|
|
992
|
+
if (stats?.hook?.name !== options.name) return;
|
|
1140
993
|
stats.hook.onFinish();
|
|
1141
994
|
stats.hook.visible = false;
|
|
1142
995
|
}
|
|
1143
996
|
onTestCaseReady(test) {
|
|
1144
997
|
// Track slow running tests only on verbose mode
|
|
1145
|
-
if (!this.options.verbose)
|
|
1146
|
-
return;
|
|
1147
|
-
}
|
|
998
|
+
if (!this.options.verbose) return;
|
|
1148
999
|
const stats = this.runningModules.get(test.module.id);
|
|
1149
|
-
if (!stats || stats.tests.has(test.id))
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1000
|
+
if (!stats || stats.tests.has(test.id)) return;
|
|
1152
1001
|
const slowTest = {
|
|
1153
1002
|
name: test.name,
|
|
1154
1003
|
visible: false,
|
|
@@ -1166,59 +1015,42 @@ class SummaryReporter {
|
|
|
1166
1015
|
}
|
|
1167
1016
|
onTestCaseResult(test) {
|
|
1168
1017
|
const stats = this.runningModules.get(test.module.id);
|
|
1169
|
-
if (!stats)
|
|
1170
|
-
return;
|
|
1171
|
-
}
|
|
1018
|
+
if (!stats) return;
|
|
1172
1019
|
stats.tests.get(test.id)?.onFinish();
|
|
1173
1020
|
stats.tests.delete(test.id);
|
|
1174
1021
|
stats.completed++;
|
|
1175
1022
|
const result = test.result();
|
|
1176
|
-
if (result?.state === "passed")
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
this.tests.failed++;
|
|
1180
|
-
} else if (!result?.state || result?.state === "skipped") {
|
|
1181
|
-
this.tests.skipped++;
|
|
1182
|
-
}
|
|
1023
|
+
if (result?.state === "passed") this.tests.passed++;
|
|
1024
|
+
else if (result?.state === "failed") this.tests.failed++;
|
|
1025
|
+
else if (!result?.state || result?.state === "skipped") this.tests.skipped++;
|
|
1183
1026
|
this.renderer.schedule();
|
|
1184
1027
|
}
|
|
1185
1028
|
onTestModuleEnd(module) {
|
|
1186
1029
|
const state = module.state();
|
|
1187
1030
|
this.modules.completed++;
|
|
1188
|
-
if (state === "passed")
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
} else if (module.task.mode === "todo" && state === "skipped") {
|
|
1193
|
-
this.modules.todo++;
|
|
1194
|
-
} else if (state === "skipped") {
|
|
1195
|
-
this.modules.skipped++;
|
|
1196
|
-
}
|
|
1031
|
+
if (state === "passed") this.modules.passed++;
|
|
1032
|
+
else if (state === "failed") this.modules.failed++;
|
|
1033
|
+
else if (module.task.mode === "todo" && state === "skipped") this.modules.todo++;
|
|
1034
|
+
else if (state === "skipped") this.modules.skipped++;
|
|
1197
1035
|
const left = this.modules.total - this.modules.completed;
|
|
1198
1036
|
// Keep finished tests visible in summary for a while if there are more tests left.
|
|
1199
1037
|
// When a new test starts in onTestModuleQueued it will take this ones place.
|
|
1200
1038
|
// This reduces flickering by making summary more stable.
|
|
1201
|
-
if (left > this.maxParallelTests) {
|
|
1202
|
-
this.finishedModules.set(module.id, setTimeout(() => {
|
|
1203
|
-
this.removeTestModule(module.id);
|
|
1204
|
-
}, FINISHED_TEST_CLEANUP_TIME_MS).unref());
|
|
1205
|
-
} else {
|
|
1206
|
-
// Run is about to end as there are less tests left than whole run had parallel at max.
|
|
1207
|
-
// Remove finished test immediately.
|
|
1039
|
+
if (left > this.maxParallelTests) this.finishedModules.set(module.id, setTimeout(() => {
|
|
1208
1040
|
this.removeTestModule(module.id);
|
|
1209
|
-
}
|
|
1041
|
+
}, FINISHED_TEST_CLEANUP_TIME_MS).unref());
|
|
1042
|
+
else
|
|
1043
|
+
// Run is about to end as there are less tests left than whole run had parallel at max.
|
|
1044
|
+
// Remove finished test immediately.
|
|
1045
|
+
this.removeTestModule(module.id);
|
|
1210
1046
|
this.renderer.schedule();
|
|
1211
1047
|
}
|
|
1212
1048
|
getHookStats({ entity }) {
|
|
1213
1049
|
// Track slow running hooks only on verbose mode
|
|
1214
|
-
if (!this.options.verbose)
|
|
1215
|
-
return;
|
|
1216
|
-
}
|
|
1050
|
+
if (!this.options.verbose) return;
|
|
1217
1051
|
const module = entity.type === "module" ? entity : entity.module;
|
|
1218
1052
|
const stats = this.runningModules.get(module.id);
|
|
1219
|
-
if (!stats)
|
|
1220
|
-
return;
|
|
1221
|
-
}
|
|
1053
|
+
if (!stats) return;
|
|
1222
1054
|
return entity.type === "test" ? stats.tests.get(entity.id) : stats;
|
|
1223
1055
|
}
|
|
1224
1056
|
createSummary() {
|
|
@@ -1234,14 +1066,10 @@ class SummaryReporter {
|
|
|
1234
1066
|
const elapsed = this.currentTime - task.startTime;
|
|
1235
1067
|
const icon = index === slowTasks.length - 1 ? F_TREE_NODE_END : F_TREE_NODE_MIDDLE;
|
|
1236
1068
|
summary.push(c.bold(c.yellow(` ${icon} `)) + task.name + c.bold(c.yellow(` ${formatTime(Math.max(0, elapsed))}`)));
|
|
1237
|
-
if (task.hook?.visible) {
|
|
1238
|
-
summary.push(c.bold(c.yellow(` ${F_TREE_NODE_END} `)) + task.hook.name);
|
|
1239
|
-
}
|
|
1069
|
+
if (task.hook?.visible) summary.push(c.bold(c.yellow(` ${F_TREE_NODE_END} `)) + task.hook.name);
|
|
1240
1070
|
}
|
|
1241
1071
|
}
|
|
1242
|
-
if (this.runningModules.size > 0)
|
|
1243
|
-
summary.push("");
|
|
1244
|
-
}
|
|
1072
|
+
if (this.runningModules.size > 0) summary.push("");
|
|
1245
1073
|
summary.push(padSummaryTitle("Test Files") + getStateString(this.modules));
|
|
1246
1074
|
summary.push(padSummaryTitle("Tests") + getStateString(this.tests));
|
|
1247
1075
|
summary.push(padSummaryTitle("Start at") + this.startTime);
|
|
@@ -1251,16 +1079,14 @@ class SummaryReporter {
|
|
|
1251
1079
|
}
|
|
1252
1080
|
startTimers() {
|
|
1253
1081
|
const start = performance.now();
|
|
1254
|
-
this.startTime = formatTimeString(new Date());
|
|
1082
|
+
this.startTime = formatTimeString(/* @__PURE__ */ new Date());
|
|
1255
1083
|
this.durationInterval = setInterval(() => {
|
|
1256
1084
|
this.currentTime = performance.now();
|
|
1257
1085
|
this.duration = this.currentTime - start;
|
|
1258
1086
|
}, DURATION_UPDATE_INTERVAL_MS).unref();
|
|
1259
1087
|
}
|
|
1260
1088
|
removeTestModule(id) {
|
|
1261
|
-
if (!id)
|
|
1262
|
-
return;
|
|
1263
|
-
}
|
|
1089
|
+
if (!id) return;
|
|
1264
1090
|
const testFile = this.runningModules.get(id);
|
|
1265
1091
|
testFile?.hook?.onFinish();
|
|
1266
1092
|
testFile?.tests?.forEach((test) => test.onFinish());
|
|
@@ -1288,12 +1114,8 @@ function getStateString(entry) {
|
|
|
1288
1114
|
].filter(Boolean).join(c.dim(" | ")) + c.gray(` (${entry.total})`);
|
|
1289
1115
|
}
|
|
1290
1116
|
function sortRunningModules(a, b) {
|
|
1291
|
-
if ((a.projectName || "") > (b.projectName || ""))
|
|
1292
|
-
|
|
1293
|
-
}
|
|
1294
|
-
if ((a.projectName || "") < (b.projectName || "")) {
|
|
1295
|
-
return -1;
|
|
1296
|
-
}
|
|
1117
|
+
if ((a.projectName || "") > (b.projectName || "")) return 1;
|
|
1118
|
+
if ((a.projectName || "") < (b.projectName || "")) return -1;
|
|
1297
1119
|
return a.filename.localeCompare(b.filename);
|
|
1298
1120
|
}
|
|
1299
1121
|
function initializeStats(module) {
|
|
@@ -1303,7 +1125,7 @@ function initializeStats(module) {
|
|
|
1303
1125
|
filename: module.task.name,
|
|
1304
1126
|
projectName: module.project.name,
|
|
1305
1127
|
projectColor: module.project.color,
|
|
1306
|
-
tests: new Map(),
|
|
1128
|
+
tests: /* @__PURE__ */ new Map(),
|
|
1307
1129
|
typecheck: !!module.task.meta.typecheck
|
|
1308
1130
|
};
|
|
1309
1131
|
}
|
|
@@ -1317,21 +1139,13 @@ class DefaultReporter extends BaseReporter {
|
|
|
1317
1139
|
summary: true,
|
|
1318
1140
|
...options
|
|
1319
1141
|
};
|
|
1320
|
-
if (!this.isTTY)
|
|
1321
|
-
|
|
1322
|
-
}
|
|
1323
|
-
if (this.options.summary) {
|
|
1324
|
-
this.summary = new SummaryReporter();
|
|
1325
|
-
}
|
|
1142
|
+
if (!this.isTTY) this.options.summary = false;
|
|
1143
|
+
if (this.options.summary) this.summary = new SummaryReporter();
|
|
1326
1144
|
}
|
|
1327
1145
|
onTestRunStart(specifications) {
|
|
1328
1146
|
if (this.isTTY) {
|
|
1329
|
-
if (this.renderSucceed ===
|
|
1330
|
-
|
|
1331
|
-
}
|
|
1332
|
-
if (this.renderSucceed !== true) {
|
|
1333
|
-
this.renderSucceed = specifications.length <= 1;
|
|
1334
|
-
}
|
|
1147
|
+
if (this.renderSucceed === void 0) this.renderSucceed = !!this.renderSucceed;
|
|
1148
|
+
if (this.renderSucceed !== true) this.renderSucceed = specifications.length <= 1;
|
|
1335
1149
|
}
|
|
1336
1150
|
this.summary?.onTestRunStart(specifications);
|
|
1337
1151
|
}
|
|
@@ -1369,8 +1183,8 @@ class DefaultReporter extends BaseReporter {
|
|
|
1369
1183
|
|
|
1370
1184
|
class DotReporter extends BaseReporter {
|
|
1371
1185
|
renderer;
|
|
1372
|
-
tests = new Map();
|
|
1373
|
-
finishedTests = new Set();
|
|
1186
|
+
tests = /* @__PURE__ */ new Map();
|
|
1187
|
+
finishedTests = /* @__PURE__ */ new Set();
|
|
1374
1188
|
onInit(ctx) {
|
|
1375
1189
|
super.onInit(ctx);
|
|
1376
1190
|
if (this.isTTY) {
|
|
@@ -1382,9 +1196,7 @@ class DotReporter extends BaseReporter {
|
|
|
1382
1196
|
}
|
|
1383
1197
|
}
|
|
1384
1198
|
printTestModule(testModule) {
|
|
1385
|
-
if (!this.isTTY)
|
|
1386
|
-
super.printTestModule(testModule);
|
|
1387
|
-
}
|
|
1199
|
+
if (!this.isTTY) super.printTestModule(testModule);
|
|
1388
1200
|
}
|
|
1389
1201
|
onWatcherRerun(files, trigger) {
|
|
1390
1202
|
this.tests.clear();
|
|
@@ -1401,15 +1213,12 @@ class DotReporter extends BaseReporter {
|
|
|
1401
1213
|
super.onFinished(files, errors);
|
|
1402
1214
|
}
|
|
1403
1215
|
onTestModuleCollected(module) {
|
|
1404
|
-
for (const test of module.children.allTests())
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
}
|
|
1216
|
+
for (const test of module.children.allTests())
|
|
1217
|
+
// Dot reporter marks pending tests as running
|
|
1218
|
+
this.onTestCaseReady(test);
|
|
1408
1219
|
}
|
|
1409
1220
|
onTestCaseReady(test) {
|
|
1410
|
-
if (this.finishedTests.has(test.id))
|
|
1411
|
-
return;
|
|
1412
|
-
}
|
|
1221
|
+
if (this.finishedTests.has(test.id)) return;
|
|
1413
1222
|
this.tests.set(test.id, test.result().state || "run");
|
|
1414
1223
|
this.renderer?.schedule();
|
|
1415
1224
|
}
|
|
@@ -1421,24 +1230,16 @@ class DotReporter extends BaseReporter {
|
|
|
1421
1230
|
}
|
|
1422
1231
|
onTestModuleEnd(testModule) {
|
|
1423
1232
|
super.onTestModuleEnd(testModule);
|
|
1424
|
-
if (!this.isTTY)
|
|
1425
|
-
return;
|
|
1426
|
-
}
|
|
1233
|
+
if (!this.isTTY) return;
|
|
1427
1234
|
const columns = this.ctx.logger.getColumns();
|
|
1428
|
-
if (this.tests.size < columns)
|
|
1429
|
-
return;
|
|
1430
|
-
}
|
|
1235
|
+
if (this.tests.size < columns) return;
|
|
1431
1236
|
const finishedTests = Array.from(this.tests).filter((entry) => entry[1] !== "pending");
|
|
1432
|
-
if (finishedTests.length < columns)
|
|
1433
|
-
return;
|
|
1434
|
-
}
|
|
1237
|
+
if (finishedTests.length < columns) return;
|
|
1435
1238
|
// Remove finished tests from state and render them in static output
|
|
1436
1239
|
const states = [];
|
|
1437
1240
|
let count = 0;
|
|
1438
1241
|
for (const [id, state] of finishedTests) {
|
|
1439
|
-
if (count++ >= columns)
|
|
1440
|
-
break;
|
|
1441
|
-
}
|
|
1242
|
+
if (count++ >= columns) break;
|
|
1442
1243
|
this.tests.delete(id);
|
|
1443
1244
|
states.push(state);
|
|
1444
1245
|
}
|
|
@@ -1529,13 +1330,11 @@ function printError(error, ctx, logger, options) {
|
|
|
1529
1330
|
parseErrorStacktrace(error) {
|
|
1530
1331
|
// browser stack trace needs to be processed differently,
|
|
1531
1332
|
// so there is a separate method for that
|
|
1532
|
-
if (options.task?.file.pool === "browser" && project.browser) {
|
|
1533
|
-
return project.browser.parseErrorStacktrace(error, { ignoreStackEntries: options.fullStack ? [] : undefined });
|
|
1534
|
-
}
|
|
1333
|
+
if (options.task?.file.pool === "browser" && project.browser) return project.browser.parseErrorStacktrace(error, { ignoreStackEntries: options.fullStack ? [] : void 0 });
|
|
1535
1334
|
// node.js stack trace already has correct source map locations
|
|
1536
1335
|
return parseErrorStacktrace(error, {
|
|
1537
1336
|
frameFilter: project.config.onStackTrace,
|
|
1538
|
-
ignoreStackEntries: options.fullStack ? [] :
|
|
1337
|
+
ignoreStackEntries: options.fullStack ? [] : void 0
|
|
1539
1338
|
});
|
|
1540
1339
|
}
|
|
1541
1340
|
});
|
|
@@ -1544,12 +1343,10 @@ function printErrorInner(error, project, options) {
|
|
|
1544
1343
|
const { showCodeFrame = true, type, printProperties = true } = options;
|
|
1545
1344
|
const logger = options.logger;
|
|
1546
1345
|
let e = error;
|
|
1547
|
-
if (isPrimitive(e)) {
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1346
|
+
if (isPrimitive(e)) e = {
|
|
1347
|
+
message: String(error).split(/\n/g)[0],
|
|
1348
|
+
stack: String(error)
|
|
1349
|
+
};
|
|
1553
1350
|
if (!e) {
|
|
1554
1351
|
const error = new Error("unknown error");
|
|
1555
1352
|
e = {
|
|
@@ -1570,21 +1367,15 @@ function printErrorInner(error, project, options) {
|
|
|
1570
1367
|
return false;
|
|
1571
1368
|
}
|
|
1572
1369
|
});
|
|
1573
|
-
if (type)
|
|
1574
|
-
printErrorType(type, project.vitest);
|
|
1575
|
-
}
|
|
1370
|
+
if (type) printErrorType(type, project.vitest);
|
|
1576
1371
|
printErrorMessage(e, logger);
|
|
1577
1372
|
if (options.screenshotPaths?.length) {
|
|
1578
1373
|
const length = options.screenshotPaths.length;
|
|
1579
1374
|
logger.error(`\nFailure screenshot${length > 1 ? "s" : ""}:`);
|
|
1580
1375
|
logger.error(options.screenshotPaths.map((p) => ` - ${c.dim(relative(process.cwd(), p))}`).join("\n"));
|
|
1581
|
-
if (!e.diff)
|
|
1582
|
-
logger.error();
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
if (e.codeFrame) {
|
|
1586
|
-
logger.error(`${e.codeFrame}\n`);
|
|
1376
|
+
if (!e.diff) logger.error();
|
|
1587
1377
|
}
|
|
1378
|
+
if (e.codeFrame) logger.error(`${e.codeFrame}\n`);
|
|
1588
1379
|
if ("__vitest_rollup_error__" in e) {
|
|
1589
1380
|
// https://github.com/vitejs/vite/blob/95020ab49e12d143262859e095025cf02423c1d9/packages/vite/src/node/server/middlewares/error.ts#L25-L36
|
|
1590
1381
|
const err = e.__vitest_rollup_error__;
|
|
@@ -1595,13 +1386,10 @@ function printErrorInner(error, project, options) {
|
|
|
1595
1386
|
].filter(Boolean).join("\n"));
|
|
1596
1387
|
}
|
|
1597
1388
|
// E.g. AssertionError from assert does not set showDiff but has both actual and expected properties
|
|
1598
|
-
if (e.diff) {
|
|
1599
|
-
logger.error(`\n${e.diff}\n`);
|
|
1600
|
-
}
|
|
1389
|
+
if (e.diff) logger.error(`\n${e.diff}\n`);
|
|
1601
1390
|
// if the error provide the frame
|
|
1602
|
-
if (e.frame)
|
|
1603
|
-
|
|
1604
|
-
} else {
|
|
1391
|
+
if (e.frame) logger.error(c.yellow(e.frame));
|
|
1392
|
+
else {
|
|
1605
1393
|
const errorProperties = printProperties ? getErrorProperties(e) : {};
|
|
1606
1394
|
printStack(logger, project, stacks, nearest, errorProperties, (s) => {
|
|
1607
1395
|
if (showCodeFrame && s === nearest && nearest) {
|
|
@@ -1614,15 +1402,11 @@ function printErrorInner(error, project, options) {
|
|
|
1614
1402
|
const testName = e.VITEST_TEST_NAME;
|
|
1615
1403
|
const afterEnvTeardown = e.VITEST_AFTER_ENV_TEARDOWN;
|
|
1616
1404
|
// testName has testPath inside
|
|
1617
|
-
if (testPath) {
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
}
|
|
1623
|
-
if (afterEnvTeardown) {
|
|
1624
|
-
logger.error(c.red("This error was caught after test environment was torn down. Make sure to cancel any running tasks before test finishes:" + "\n- cancel timeouts using clearTimeout and clearInterval" + "\n- wait for promises to resolve using the await keyword"));
|
|
1625
|
-
}
|
|
1405
|
+
if (testPath) logger.error(c.red(`This error originated in "${c.bold(relative(project.config.root, testPath))}" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.`));
|
|
1406
|
+
if (testName) logger.error(c.red(`The latest test that might've caused the error is "${c.bold(testName)}". It might mean one of the following:
|
|
1407
|
+
- The error was thrown, while Vitest was running this test.
|
|
1408
|
+
- If the error occurred after the test had been completed, this was the last documented test before it was thrown.`));
|
|
1409
|
+
if (afterEnvTeardown) logger.error(c.red("This error was caught after test environment was torn down. Make sure to cancel any running tasks before test finishes:\n- cancel timeouts using clearTimeout and clearInterval\n- wait for promises to resolve using the await keyword"));
|
|
1626
1410
|
if (typeof e.cause === "object" && e.cause && "name" in e.cause) {
|
|
1627
1411
|
e.cause.name = `Caused by: ${e.cause.name}`;
|
|
1628
1412
|
printErrorInner(e.cause, project, {
|
|
@@ -1631,17 +1415,15 @@ function printErrorInner(error, project, options) {
|
|
|
1631
1415
|
parseErrorStacktrace: options.parseErrorStacktrace
|
|
1632
1416
|
});
|
|
1633
1417
|
}
|
|
1634
|
-
handleImportOutsideModuleError(e.stack ||
|
|
1418
|
+
handleImportOutsideModuleError(e.stack || "", logger);
|
|
1635
1419
|
return { nearest };
|
|
1636
1420
|
}
|
|
1637
1421
|
function printErrorType(type, ctx) {
|
|
1638
1422
|
ctx.logger.error(`\n${errorBanner(type)}`);
|
|
1639
1423
|
}
|
|
1640
1424
|
const skipErrorProperties = new Set([
|
|
1641
|
-
"nameStr",
|
|
1642
1425
|
"cause",
|
|
1643
1426
|
"stacks",
|
|
1644
|
-
"stackStr",
|
|
1645
1427
|
"type",
|
|
1646
1428
|
"showDiff",
|
|
1647
1429
|
"ok",
|
|
@@ -1660,44 +1442,35 @@ const skipErrorProperties = new Set([
|
|
|
1660
1442
|
"VITEST_TEST_NAME",
|
|
1661
1443
|
"VITEST_TEST_PATH",
|
|
1662
1444
|
"VITEST_AFTER_ENV_TEARDOWN",
|
|
1445
|
+
"__vitest_rollup_error__",
|
|
1663
1446
|
...Object.getOwnPropertyNames(Error.prototype),
|
|
1664
1447
|
...Object.getOwnPropertyNames(Object.prototype)
|
|
1665
1448
|
]);
|
|
1666
1449
|
function getErrorProperties(e) {
|
|
1667
1450
|
const errorObject = Object.create(null);
|
|
1668
|
-
if (e.name === "AssertionError")
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
if (key === "stack" && e[key] != null && typeof e[key] !== "string") {
|
|
1674
|
-
errorObject[key] = e[key];
|
|
1675
|
-
} else if (key !== "stack" && !skipErrorProperties.has(key)) {
|
|
1676
|
-
errorObject[key] = e[key];
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1451
|
+
if (e.name === "AssertionError") return errorObject;
|
|
1452
|
+
for (const key of Object.getOwnPropertyNames(e))
|
|
1453
|
+
// print the original stack if it was ever changed manually by the user
|
|
1454
|
+
if (key === "stack" && e[key] != null && typeof e[key] !== "string") errorObject[key] = e[key];
|
|
1455
|
+
else if (key !== "stack" && !skipErrorProperties.has(key)) errorObject[key] = e[key];
|
|
1679
1456
|
return errorObject;
|
|
1680
1457
|
}
|
|
1681
1458
|
const esmErrors = ["Cannot use import statement outside a module", "Unexpected token 'export'"];
|
|
1682
1459
|
function handleImportOutsideModuleError(stack, logger) {
|
|
1683
|
-
if (!esmErrors.some((e) => stack.includes(e)))
|
|
1684
|
-
return;
|
|
1685
|
-
}
|
|
1460
|
+
if (!esmErrors.some((e) => stack.includes(e))) return;
|
|
1686
1461
|
const path = normalize(stack.split("\n")[0].trim());
|
|
1687
1462
|
let name = path.split("/node_modules/").pop() || "";
|
|
1688
|
-
if (name?.startsWith("@"))
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
if (name) {
|
|
1694
|
-
printModuleWarningForPackage(logger, path, name);
|
|
1695
|
-
} else {
|
|
1696
|
-
printModuleWarningForSourceCode(logger, path);
|
|
1697
|
-
}
|
|
1463
|
+
if (name?.startsWith("@")) name = name.split("/").slice(0, 2).join("/");
|
|
1464
|
+
else name = name.split("/")[0];
|
|
1465
|
+
if (name) printModuleWarningForPackage(logger, path, name);
|
|
1466
|
+
else printModuleWarningForSourceCode(logger, path);
|
|
1698
1467
|
}
|
|
1699
1468
|
function printModuleWarningForPackage(logger, path, name) {
|
|
1700
|
-
logger.error(c.yellow(`Module ${path} seems to be an ES Module but shipped in a CommonJS package.
|
|
1469
|
+
logger.error(c.yellow(`Module ${path} seems to be an ES Module but shipped in a CommonJS package. You might want to create an issue to the package ${c.bold(`"${name}"`)} asking them to ship the file in .mjs extension or add "type": "module" in their package.json.
|
|
1470
|
+
|
|
1471
|
+
As a temporary workaround you can try to inline the package by updating your config:
|
|
1472
|
+
|
|
1473
|
+
` + c.gray(c.dim("// vitest.config.js")) + "\n" + c.green(`export default {
|
|
1701
1474
|
test: {
|
|
1702
1475
|
server: {
|
|
1703
1476
|
deps: {
|
|
@@ -1710,20 +1483,18 @@ function printModuleWarningForPackage(logger, path, name) {
|
|
|
1710
1483
|
}\n`)));
|
|
1711
1484
|
}
|
|
1712
1485
|
function printModuleWarningForSourceCode(logger, path) {
|
|
1713
|
-
logger.error(c.yellow(`Module ${path} seems to be an ES Module but shipped in a CommonJS package.
|
|
1486
|
+
logger.error(c.yellow(`Module ${path} seems to be an ES Module but shipped in a CommonJS package. To fix this issue, change the file extension to .mjs or add "type": "module" in your package.json.`));
|
|
1714
1487
|
}
|
|
1715
1488
|
function printErrorMessage(error, logger) {
|
|
1716
|
-
const errorName = error.name ||
|
|
1489
|
+
const errorName = error.name || "Unknown Error";
|
|
1717
1490
|
if (!error.message) {
|
|
1718
1491
|
logger.error(error);
|
|
1719
1492
|
return;
|
|
1720
1493
|
}
|
|
1721
|
-
if (error.message.length > 5e3)
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
logger.error(c.red(`${c.bold(errorName)}: ${error.message}`));
|
|
1726
|
-
}
|
|
1494
|
+
if (error.message.length > 5e3)
|
|
1495
|
+
// Protect against infinite stack trace in tinyrainbow
|
|
1496
|
+
logger.error(`${c.red(c.bold(errorName))}: ${error.message}`);
|
|
1497
|
+
else logger.error(c.red(`${c.bold(errorName)}: ${error.message}`));
|
|
1727
1498
|
}
|
|
1728
1499
|
function printStack(logger, project, stack, highlight, errorProperties, onStack) {
|
|
1729
1500
|
for (const frame of stack) {
|
|
@@ -1732,9 +1503,7 @@ function printStack(logger, project, stack, highlight, errorProperties, onStack)
|
|
|
1732
1503
|
logger.error(color(` ${c.dim(F_POINTER)} ${[frame.method, `${path}:${c.dim(`${frame.line}:${frame.column}`)}`].filter(Boolean).join(" ")}`));
|
|
1733
1504
|
onStack?.(frame);
|
|
1734
1505
|
}
|
|
1735
|
-
if (stack.length)
|
|
1736
|
-
logger.error();
|
|
1737
|
-
}
|
|
1506
|
+
if (stack.length) logger.error();
|
|
1738
1507
|
if (hasProperties(errorProperties)) {
|
|
1739
1508
|
logger.error(c.red(c.dim(divider())));
|
|
1740
1509
|
const propertiesString = inspect(errorProperties);
|
|
@@ -1743,9 +1512,7 @@ function printStack(logger, project, stack, highlight, errorProperties, onStack)
|
|
|
1743
1512
|
}
|
|
1744
1513
|
function hasProperties(obj) {
|
|
1745
1514
|
// eslint-disable-next-line no-unreachable-loop
|
|
1746
|
-
for (const _key in obj)
|
|
1747
|
-
return true;
|
|
1748
|
-
}
|
|
1515
|
+
for (const _key in obj) return true;
|
|
1749
1516
|
return false;
|
|
1750
1517
|
}
|
|
1751
1518
|
function generateCodeFrame(source, indent = 0, loc, range = 2) {
|
|
@@ -1760,14 +1527,10 @@ function generateCodeFrame(source, indent = 0, loc, range = 2) {
|
|
|
1760
1527
|
count += lines[i].length + nl;
|
|
1761
1528
|
if (count >= start) {
|
|
1762
1529
|
for (let j = i - range; j <= i + range || end > count; j++) {
|
|
1763
|
-
if (j < 0 || j >= lines.length)
|
|
1764
|
-
continue;
|
|
1765
|
-
}
|
|
1530
|
+
if (j < 0 || j >= lines.length) continue;
|
|
1766
1531
|
const lineLength = lines[j].length;
|
|
1767
1532
|
// too long, maybe it's a minified file, skip for codeframe
|
|
1768
|
-
if (stripVTControlCharacters(lines[j]).length > 200)
|
|
1769
|
-
return "";
|
|
1770
|
-
}
|
|
1533
|
+
if (stripVTControlCharacters(lines[j]).length > 200) return "";
|
|
1771
1534
|
res.push(lineNo(j + 1) + truncateString(lines[j].replace(/\t/g, " "), columns - 5 - indent));
|
|
1772
1535
|
if (j === i) {
|
|
1773
1536
|
// push underline
|
|
@@ -1785,9 +1548,7 @@ function generateCodeFrame(source, indent = 0, loc, range = 2) {
|
|
|
1785
1548
|
break;
|
|
1786
1549
|
}
|
|
1787
1550
|
}
|
|
1788
|
-
if (indent)
|
|
1789
|
-
res = res.map((line) => " ".repeat(indent) + line);
|
|
1790
|
-
}
|
|
1551
|
+
if (indent) res = res.map((line) => " ".repeat(indent) + line);
|
|
1791
1552
|
return res.join("\n");
|
|
1792
1553
|
}
|
|
1793
1554
|
function lineNo(no = "") {
|
|
@@ -1795,7 +1556,7 @@ function lineNo(no = "") {
|
|
|
1795
1556
|
}
|
|
1796
1557
|
|
|
1797
1558
|
class GithubActionsReporter {
|
|
1798
|
-
ctx =
|
|
1559
|
+
ctx = void 0;
|
|
1799
1560
|
options;
|
|
1800
1561
|
constructor(options = {}) {
|
|
1801
1562
|
this.options = options;
|
|
@@ -1803,32 +1564,41 @@ class GithubActionsReporter {
|
|
|
1803
1564
|
onInit(ctx) {
|
|
1804
1565
|
this.ctx = ctx;
|
|
1805
1566
|
}
|
|
1567
|
+
onTestCaseAnnotate(testCase, annotation) {
|
|
1568
|
+
if (!annotation.location) return;
|
|
1569
|
+
const type = getTitle(annotation.type);
|
|
1570
|
+
const formatted = formatMessage({
|
|
1571
|
+
command: getType(annotation.type),
|
|
1572
|
+
properties: {
|
|
1573
|
+
file: annotation.location.file,
|
|
1574
|
+
line: String(annotation.location.line),
|
|
1575
|
+
column: String(annotation.location.column),
|
|
1576
|
+
...type && { title: type }
|
|
1577
|
+
},
|
|
1578
|
+
message: stripVTControlCharacters(annotation.message)
|
|
1579
|
+
});
|
|
1580
|
+
this.ctx.logger.log(`\n${formatted}`);
|
|
1581
|
+
}
|
|
1806
1582
|
onFinished(files = [], errors = []) {
|
|
1807
1583
|
// collect all errors and associate them with projects
|
|
1808
1584
|
const projectErrors = new Array();
|
|
1809
|
-
for (const error of errors) {
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1585
|
+
for (const error of errors) projectErrors.push({
|
|
1586
|
+
project: this.ctx.getRootProject(),
|
|
1587
|
+
title: "Unhandled error",
|
|
1588
|
+
error
|
|
1589
|
+
});
|
|
1816
1590
|
for (const file of files) {
|
|
1817
1591
|
const tasks = getTasks(file);
|
|
1818
1592
|
const project = this.ctx.getProjectByName(file.projectName || "");
|
|
1819
1593
|
for (const task of tasks) {
|
|
1820
|
-
if (task.result?.state !== "fail")
|
|
1821
|
-
continue;
|
|
1822
|
-
}
|
|
1594
|
+
if (task.result?.state !== "fail") continue;
|
|
1823
1595
|
const title = getFullName(task, " > ");
|
|
1824
|
-
for (const error of task.result?.errors ?? []) {
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
});
|
|
1831
|
-
}
|
|
1596
|
+
for (const error of task.result?.errors ?? []) projectErrors.push({
|
|
1597
|
+
project,
|
|
1598
|
+
title,
|
|
1599
|
+
error,
|
|
1600
|
+
file
|
|
1601
|
+
});
|
|
1832
1602
|
}
|
|
1833
1603
|
}
|
|
1834
1604
|
const onWritePath = this.options.onWritePath ?? defaultOnWritePath;
|
|
@@ -1839,9 +1609,7 @@ class GithubActionsReporter {
|
|
|
1839
1609
|
task: file
|
|
1840
1610
|
});
|
|
1841
1611
|
const stack = result?.nearest;
|
|
1842
|
-
if (!stack)
|
|
1843
|
-
continue;
|
|
1844
|
-
}
|
|
1612
|
+
if (!stack) continue;
|
|
1845
1613
|
const formatted = formatMessage({
|
|
1846
1614
|
command: "error",
|
|
1847
1615
|
properties: {
|
|
@@ -1856,6 +1624,19 @@ class GithubActionsReporter {
|
|
|
1856
1624
|
}
|
|
1857
1625
|
}
|
|
1858
1626
|
}
|
|
1627
|
+
const BUILT_IN_TYPES = [
|
|
1628
|
+
"notice",
|
|
1629
|
+
"error",
|
|
1630
|
+
"warning"
|
|
1631
|
+
];
|
|
1632
|
+
function getTitle(type) {
|
|
1633
|
+
if (BUILT_IN_TYPES.includes(type)) return void 0;
|
|
1634
|
+
return type;
|
|
1635
|
+
}
|
|
1636
|
+
function getType(type) {
|
|
1637
|
+
if (BUILT_IN_TYPES.includes(type)) return type;
|
|
1638
|
+
return "notice";
|
|
1639
|
+
}
|
|
1859
1640
|
function defaultOnWritePath(path) {
|
|
1860
1641
|
return path;
|
|
1861
1642
|
}
|
|
@@ -1926,9 +1707,7 @@ class JsonReporter {
|
|
|
1926
1707
|
for (const file of files) {
|
|
1927
1708
|
const tests = getTests([file]);
|
|
1928
1709
|
let startTime = tests.reduce((prev, next) => Math.min(prev, next.result?.startTime ?? Number.POSITIVE_INFINITY), Number.POSITIVE_INFINITY);
|
|
1929
|
-
if (startTime === Number.POSITIVE_INFINITY)
|
|
1930
|
-
startTime = this.start;
|
|
1931
|
-
}
|
|
1710
|
+
if (startTime === Number.POSITIVE_INFINITY) startTime = this.start;
|
|
1932
1711
|
const endTime = tests.reduce((prev, next) => Math.max(prev, (next.result?.startTime ?? 0) + (next.result?.duration ?? 0)), startTime);
|
|
1933
1712
|
const assertionResults = tests.map((t) => {
|
|
1934
1713
|
const ancestorTitles = [];
|
|
@@ -1949,9 +1728,7 @@ class JsonReporter {
|
|
|
1949
1728
|
meta: t.meta
|
|
1950
1729
|
};
|
|
1951
1730
|
});
|
|
1952
|
-
if (tests.some((t) => t.result?.state === "run" || t.result?.state === "queued"))
|
|
1953
|
-
this.ctx.logger.warn("WARNING: Some tests are still running when generating the JSON report." + "This is likely an internal bug in Vitest." + "Please report it to https://github.com/vitest-dev/vitest/issues");
|
|
1954
|
-
}
|
|
1731
|
+
if (tests.some((t) => t.result?.state === "run" || t.result?.state === "queued")) this.ctx.logger.warn("WARNING: Some tests are still running when generating the JSON report.This is likely an internal bug in Vitest.Please report it to https://github.com/vitest-dev/vitest/issues");
|
|
1955
1732
|
const hasFailedTests = tests.some((t) => t.result?.state === "fail");
|
|
1956
1733
|
testResults.push({
|
|
1957
1734
|
assertionResults,
|
|
@@ -1993,14 +1770,10 @@ class JsonReporter {
|
|
|
1993
1770
|
if (outputFile) {
|
|
1994
1771
|
const reportFile = resolve(this.ctx.config.root, outputFile);
|
|
1995
1772
|
const outputDirectory = dirname(reportFile);
|
|
1996
|
-
if (!existsSync(outputDirectory)) {
|
|
1997
|
-
await promises.mkdir(outputDirectory, { recursive: true });
|
|
1998
|
-
}
|
|
1773
|
+
if (!existsSync(outputDirectory)) await promises.mkdir(outputDirectory, { recursive: true });
|
|
1999
1774
|
await promises.writeFile(reportFile, report, "utf-8");
|
|
2000
1775
|
this.ctx.logger.log(`JSON report written to ${reportFile}`);
|
|
2001
|
-
} else
|
|
2002
|
-
this.ctx.logger.log(report);
|
|
2003
|
-
}
|
|
1776
|
+
} else this.ctx.logger.log(report);
|
|
2004
1777
|
}
|
|
2005
1778
|
}
|
|
2006
1779
|
|
|
@@ -2022,14 +1795,11 @@ class IndentedLogger {
|
|
|
2022
1795
|
|
|
2023
1796
|
function flattenTasks$1(task, baseName = "") {
|
|
2024
1797
|
const base = baseName ? `${baseName} > ` : "";
|
|
2025
|
-
if (task.type === "suite") {
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
name: `${base}${task.name}`
|
|
2031
|
-
}];
|
|
2032
|
-
}
|
|
1798
|
+
if (task.type === "suite") return task.tasks.flatMap((child) => flattenTasks$1(child, `${base}${task.name}`));
|
|
1799
|
+
else return [{
|
|
1800
|
+
...task,
|
|
1801
|
+
name: `${base}${task.name}`
|
|
1802
|
+
}];
|
|
2033
1803
|
}
|
|
2034
1804
|
// https://gist.github.com/john-doherty/b9195065884cdbfd2017a4756e6409cc
|
|
2035
1805
|
function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
|
|
@@ -2039,7 +1809,7 @@ function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
|
|
|
2039
1809
|
// remove everything discouraged by XML 1.0 specifications
|
|
2040
1810
|
regex = new RegExp(
|
|
2041
1811
|
/* eslint-disable regexp/prefer-character-class, regexp/no-obscure-range, regexp/no-useless-non-capturing-group */
|
|
2042
|
-
"([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\
|
|
1812
|
+
"([\\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]))",
|
|
2043
1813
|
"g"
|
|
2044
1814
|
/* eslint-enable */
|
|
2045
1815
|
);
|
|
@@ -2065,7 +1835,7 @@ class JUnitReporter {
|
|
|
2065
1835
|
reportFile;
|
|
2066
1836
|
baseLog;
|
|
2067
1837
|
logger;
|
|
2068
|
-
_timeStart = new Date();
|
|
1838
|
+
_timeStart = /* @__PURE__ */ new Date();
|
|
2069
1839
|
fileFd;
|
|
2070
1840
|
options;
|
|
2071
1841
|
constructor(options) {
|
|
@@ -2078,30 +1848,22 @@ class JUnitReporter {
|
|
|
2078
1848
|
if (outputFile) {
|
|
2079
1849
|
this.reportFile = resolve(this.ctx.config.root, outputFile);
|
|
2080
1850
|
const outputDirectory = dirname(this.reportFile);
|
|
2081
|
-
if (!existsSync(outputDirectory)) {
|
|
2082
|
-
await promises.mkdir(outputDirectory, { recursive: true });
|
|
2083
|
-
}
|
|
1851
|
+
if (!existsSync(outputDirectory)) await promises.mkdir(outputDirectory, { recursive: true });
|
|
2084
1852
|
const fileFd = await promises.open(this.reportFile, "w+");
|
|
2085
1853
|
this.fileFd = fileFd;
|
|
2086
1854
|
this.baseLog = async (text) => {
|
|
2087
|
-
if (!this.fileFd)
|
|
2088
|
-
this.fileFd = await promises.open(this.reportFile, "w+");
|
|
2089
|
-
}
|
|
1855
|
+
if (!this.fileFd) this.fileFd = await promises.open(this.reportFile, "w+");
|
|
2090
1856
|
await promises.writeFile(this.fileFd, `${text}\n`);
|
|
2091
1857
|
};
|
|
2092
|
-
} else
|
|
2093
|
-
|
|
2094
|
-
}
|
|
2095
|
-
this._timeStart = new Date();
|
|
1858
|
+
} else this.baseLog = async (text) => this.ctx.logger.log(text);
|
|
1859
|
+
this._timeStart = /* @__PURE__ */ new Date();
|
|
2096
1860
|
this.logger = new IndentedLogger(this.baseLog);
|
|
2097
1861
|
}
|
|
2098
1862
|
async writeElement(name, attrs, children) {
|
|
2099
1863
|
const pairs = [];
|
|
2100
1864
|
for (const key in attrs) {
|
|
2101
1865
|
const attr = attrs[key];
|
|
2102
|
-
if (attr ===
|
|
2103
|
-
continue;
|
|
2104
|
-
}
|
|
1866
|
+
if (attr === void 0) continue;
|
|
2105
1867
|
pairs.push(`${key}="${escapeXML(attr)}"`);
|
|
2106
1868
|
}
|
|
2107
1869
|
await this.logger.log(`<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`);
|
|
@@ -2111,18 +1873,12 @@ class JUnitReporter {
|
|
|
2111
1873
|
await this.logger.log(`</${name}>`);
|
|
2112
1874
|
}
|
|
2113
1875
|
async writeLogs(task, type) {
|
|
2114
|
-
if (task.logs == null || task.logs.length === 0)
|
|
2115
|
-
return;
|
|
2116
|
-
}
|
|
1876
|
+
if (task.logs == null || task.logs.length === 0) return;
|
|
2117
1877
|
const logType = type === "err" ? "stderr" : "stdout";
|
|
2118
1878
|
const logs = task.logs.filter((log) => log.type === logType);
|
|
2119
|
-
if (logs.length === 0)
|
|
2120
|
-
return;
|
|
2121
|
-
}
|
|
1879
|
+
if (logs.length === 0) return;
|
|
2122
1880
|
await this.writeElement(`system-${type}`, {}, async () => {
|
|
2123
|
-
for (const log of logs)
|
|
2124
|
-
await this.baseLog(escapeXML(log.content));
|
|
2125
|
-
}
|
|
1881
|
+
for (const log of logs) await this.baseLog(escapeXML(log.content));
|
|
2126
1882
|
});
|
|
2127
1883
|
}
|
|
2128
1884
|
async writeTasks(tasks, filename) {
|
|
@@ -2132,16 +1888,12 @@ class JUnitReporter {
|
|
|
2132
1888
|
filename: task.file.name,
|
|
2133
1889
|
filepath: task.file.filepath
|
|
2134
1890
|
};
|
|
2135
|
-
if (typeof this.options.classnameTemplate === "function")
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
classname = this.options.classnameTemplate.replace(/\{filename\}/g, templateVars.filename).replace(/\{filepath\}/g, templateVars.filepath);
|
|
2139
|
-
} else if (typeof this.options.classname === "string") {
|
|
2140
|
-
classname = this.options.classname;
|
|
2141
|
-
}
|
|
1891
|
+
if (typeof this.options.classnameTemplate === "function") classname = this.options.classnameTemplate(templateVars);
|
|
1892
|
+
else if (typeof this.options.classnameTemplate === "string") classname = this.options.classnameTemplate.replace(/\{filename\}/g, templateVars.filename).replace(/\{filepath\}/g, templateVars.filepath);
|
|
1893
|
+
else if (typeof this.options.classname === "string") classname = this.options.classname;
|
|
2142
1894
|
await this.writeElement("testcase", {
|
|
2143
1895
|
classname,
|
|
2144
|
-
file: this.options.addFileAttribute ? filename :
|
|
1896
|
+
file: this.options.addFileAttribute ? filename : void 0,
|
|
2145
1897
|
name: task.name,
|
|
2146
1898
|
time: getDuration(task)
|
|
2147
1899
|
}, async () => {
|
|
@@ -2149,26 +1901,30 @@ class JUnitReporter {
|
|
|
2149
1901
|
await this.writeLogs(task, "out");
|
|
2150
1902
|
await this.writeLogs(task, "err");
|
|
2151
1903
|
}
|
|
2152
|
-
if (task.mode === "skip" || task.mode === "todo")
|
|
2153
|
-
|
|
1904
|
+
if (task.mode === "skip" || task.mode === "todo") await this.logger.log("<skipped/>");
|
|
1905
|
+
if (task.type === "test" && task.annotations.length) {
|
|
1906
|
+
await this.logger.log("<properties>");
|
|
1907
|
+
this.logger.indent();
|
|
1908
|
+
for (const annotation of task.annotations) {
|
|
1909
|
+
await this.logger.log(`<property name="${escapeXML(annotation.type)}" value="${escapeXML(annotation.message)}">`);
|
|
1910
|
+
await this.logger.log("</property>");
|
|
1911
|
+
}
|
|
1912
|
+
this.logger.unindent();
|
|
1913
|
+
await this.logger.log("</properties>");
|
|
2154
1914
|
}
|
|
2155
1915
|
if (task.result?.state === "fail") {
|
|
2156
1916
|
const errors = task.result.errors || [];
|
|
2157
|
-
for (const error of errors) {
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
const result = capturePrintError(error, this.ctx, {
|
|
2166
|
-
project: this.ctx.getProjectByName(task.file.projectName || ""),
|
|
2167
|
-
task
|
|
2168
|
-
});
|
|
2169
|
-
await this.baseLog(escapeXML(stripVTControlCharacters(result.output.trim())));
|
|
1917
|
+
for (const error of errors) await this.writeElement("failure", {
|
|
1918
|
+
message: error?.message,
|
|
1919
|
+
type: error?.name
|
|
1920
|
+
}, async () => {
|
|
1921
|
+
if (!error) return;
|
|
1922
|
+
const result = capturePrintError(error, this.ctx, {
|
|
1923
|
+
project: this.ctx.getProjectByName(task.file.projectName || ""),
|
|
1924
|
+
task
|
|
2170
1925
|
});
|
|
2171
|
-
|
|
1926
|
+
await this.baseLog(escapeXML(stripVTControlCharacters(result.output.trim())));
|
|
1927
|
+
});
|
|
2172
1928
|
}
|
|
2173
1929
|
});
|
|
2174
1930
|
}
|
|
@@ -2190,11 +1946,9 @@ class JUnitReporter {
|
|
|
2190
1946
|
});
|
|
2191
1947
|
// inject failed suites to surface errors during beforeAll/afterAll
|
|
2192
1948
|
const suites = getSuites(file);
|
|
2193
|
-
for (const suite of suites) {
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
stats.failures += 1;
|
|
2197
|
-
}
|
|
1949
|
+
for (const suite of suites) if (suite.result?.errors) {
|
|
1950
|
+
tasks.push(suite);
|
|
1951
|
+
stats.failures += 1;
|
|
2198
1952
|
}
|
|
2199
1953
|
// If there are no tests, but the file failed to load, we still want to report it as a failure
|
|
2200
1954
|
if (tasks.length === 0 && file.result?.state === "fail") {
|
|
@@ -2209,7 +1963,8 @@ class JUnitReporter {
|
|
|
2209
1963
|
timeout: 0,
|
|
2210
1964
|
context: null,
|
|
2211
1965
|
suite: null,
|
|
2212
|
-
file: null
|
|
1966
|
+
file: null,
|
|
1967
|
+
annotations: []
|
|
2213
1968
|
});
|
|
2214
1969
|
}
|
|
2215
1970
|
return {
|
|
@@ -2238,7 +1993,7 @@ class JUnitReporter {
|
|
|
2238
1993
|
const filename = relative(this.ctx.config.root, file.filepath);
|
|
2239
1994
|
await this.writeElement("testsuite", {
|
|
2240
1995
|
name: filename,
|
|
2241
|
-
timestamp: new Date().toISOString(),
|
|
1996
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2242
1997
|
hostname: hostname(),
|
|
2243
1998
|
tests: file.tasks.length,
|
|
2244
1999
|
failures: file.stats.failures,
|
|
@@ -2250,11 +2005,9 @@ class JUnitReporter {
|
|
|
2250
2005
|
});
|
|
2251
2006
|
}
|
|
2252
2007
|
});
|
|
2253
|
-
if (this.reportFile) {
|
|
2254
|
-
this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
|
|
2255
|
-
}
|
|
2008
|
+
if (this.reportFile) this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
|
|
2256
2009
|
await this.fileFd?.close();
|
|
2257
|
-
this.fileFd =
|
|
2010
|
+
this.fileFd = void 0;
|
|
2258
2011
|
}
|
|
2259
2012
|
}
|
|
2260
2013
|
|
|
@@ -2272,24 +2025,18 @@ class TapReporter {
|
|
|
2272
2025
|
this.logger = new IndentedLogger(ctx.logger.log.bind(ctx.logger));
|
|
2273
2026
|
}
|
|
2274
2027
|
static getComment(task) {
|
|
2275
|
-
if (task.mode === "skip")
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
} else if (task.result?.duration != null) {
|
|
2280
|
-
return ` # time=${task.result.duration.toFixed(2)}ms`;
|
|
2281
|
-
} else {
|
|
2282
|
-
return "";
|
|
2283
|
-
}
|
|
2028
|
+
if (task.mode === "skip") return " # SKIP";
|
|
2029
|
+
else if (task.mode === "todo") return " # TODO";
|
|
2030
|
+
else if (task.result?.duration != null) return ` # time=${task.result.duration.toFixed(2)}ms`;
|
|
2031
|
+
else return "";
|
|
2284
2032
|
}
|
|
2285
2033
|
logErrorDetails(error, stack) {
|
|
2286
|
-
const errorName = error.name ||
|
|
2034
|
+
const errorName = error.name || "Unknown Error";
|
|
2287
2035
|
this.logger.log(`name: ${yamlString(String(errorName))}`);
|
|
2288
2036
|
this.logger.log(`message: ${yamlString(String(error.message))}`);
|
|
2289
|
-
if (stack)
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
}
|
|
2037
|
+
if (stack)
|
|
2038
|
+
// For compatibility with tap-mocha-reporter
|
|
2039
|
+
this.logger.log(`stack: ${yamlString(`${stack.file}:${stack.line}:${stack.column}`)}`);
|
|
2293
2040
|
}
|
|
2294
2041
|
logTasks(tasks) {
|
|
2295
2042
|
this.logger.log(`1..${tasks.length}`);
|
|
@@ -2306,6 +2053,13 @@ class TapReporter {
|
|
|
2306
2053
|
} else {
|
|
2307
2054
|
this.logger.log(`${ok} ${id} - ${tapString(task.name)}${comment}`);
|
|
2308
2055
|
const project = this.ctx.getProjectByName(task.file.projectName || "");
|
|
2056
|
+
if (task.type === "test" && task.annotations) {
|
|
2057
|
+
this.logger.indent();
|
|
2058
|
+
task.annotations.forEach(({ type, message }) => {
|
|
2059
|
+
this.logger.log(`# ${type}: ${message}`);
|
|
2060
|
+
});
|
|
2061
|
+
this.logger.unindent();
|
|
2062
|
+
}
|
|
2309
2063
|
if (task.result?.state === "fail" && task.result.errors) {
|
|
2310
2064
|
this.logger.indent();
|
|
2311
2065
|
task.result.errors.forEach((error) => {
|
|
@@ -2316,9 +2070,7 @@ class TapReporter {
|
|
|
2316
2070
|
this.logger.indent();
|
|
2317
2071
|
this.logErrorDetails(error);
|
|
2318
2072
|
this.logger.unindent();
|
|
2319
|
-
if (stack) {
|
|
2320
|
-
this.logger.log(`at: ${yamlString(`${stack.file}:${stack.line}:${stack.column}`)}`);
|
|
2321
|
-
}
|
|
2073
|
+
if (stack) this.logger.log(`at: ${yamlString(`${stack.file}:${stack.line}:${stack.column}`)}`);
|
|
2322
2074
|
if (error.showDiff) {
|
|
2323
2075
|
this.logger.log(`actual: ${yamlString(error.actual)}`);
|
|
2324
2076
|
this.logger.log(`expected: ${yamlString(error.expected)}`);
|
|
@@ -2338,14 +2090,11 @@ class TapReporter {
|
|
|
2338
2090
|
|
|
2339
2091
|
function flattenTasks(task, baseName = "") {
|
|
2340
2092
|
const base = baseName ? `${baseName} > ` : "";
|
|
2341
|
-
if (task.type === "suite" && task.tasks.length > 0) {
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
name: `${base}${task.name}`
|
|
2347
|
-
}];
|
|
2348
|
-
}
|
|
2093
|
+
if (task.type === "suite" && task.tasks.length > 0) return task.tasks.flatMap((child) => flattenTasks(child, `${base}${task.name}`));
|
|
2094
|
+
else return [{
|
|
2095
|
+
...task,
|
|
2096
|
+
name: `${base}${task.name}`
|
|
2097
|
+
}];
|
|
2349
2098
|
}
|
|
2350
2099
|
class TapFlatReporter extends TapReporter {
|
|
2351
2100
|
onInit(ctx) {
|
|
@@ -2366,37 +2115,28 @@ class VerboseReporter extends DefaultReporter {
|
|
|
2366
2115
|
// but don't print it in the CLI because we
|
|
2367
2116
|
// print all the tests when they finish
|
|
2368
2117
|
// instead of printing them when the test file finishes
|
|
2369
|
-
if (this.isTTY)
|
|
2370
|
-
return super.printTestModule(module);
|
|
2371
|
-
}
|
|
2118
|
+
if (this.isTTY) return super.printTestModule(module);
|
|
2372
2119
|
}
|
|
2373
2120
|
onTestCaseResult(test) {
|
|
2374
2121
|
super.onTestCaseResult(test);
|
|
2375
2122
|
// don't print tests in TTY as they go, only print them
|
|
2376
2123
|
// in the CLI when they finish
|
|
2377
|
-
if (this.isTTY)
|
|
2378
|
-
return;
|
|
2379
|
-
}
|
|
2124
|
+
if (this.isTTY) return;
|
|
2380
2125
|
const testResult = test.result();
|
|
2381
|
-
if (this.ctx.config.hideSkippedTests && testResult.state === "skipped")
|
|
2382
|
-
return;
|
|
2383
|
-
}
|
|
2126
|
+
if (this.ctx.config.hideSkippedTests && testResult.state === "skipped") return;
|
|
2384
2127
|
let title = ` ${getStateSymbol(test.task)} `;
|
|
2385
|
-
if (test.project.name)
|
|
2386
|
-
title += formatProjectName(test.project);
|
|
2387
|
-
}
|
|
2128
|
+
if (test.project.name) title += formatProjectName(test.project);
|
|
2388
2129
|
title += getFullName(test.task, c.dim(" > "));
|
|
2389
2130
|
title += this.getDurationPrefix(test.task);
|
|
2390
2131
|
const diagnostic = test.diagnostic();
|
|
2391
|
-
if (diagnostic?.heap != null) {
|
|
2392
|
-
|
|
2393
|
-
}
|
|
2394
|
-
if (testResult.state === "skipped" && testResult.note) {
|
|
2395
|
-
title += c.dim(c.gray(` [${testResult.note}]`));
|
|
2396
|
-
}
|
|
2132
|
+
if (diagnostic?.heap != null) title += c.magenta(` ${Math.floor(diagnostic.heap / 1024 / 1024)} MB heap used`);
|
|
2133
|
+
if (testResult.state === "skipped" && testResult.note) title += c.dim(c.gray(` [${testResult.note}]`));
|
|
2397
2134
|
this.log(title);
|
|
2398
|
-
if (testResult.state === "failed") {
|
|
2399
|
-
|
|
2135
|
+
if (testResult.state === "failed") testResult.errors.forEach((error) => this.log(c.red(` ${F_RIGHT} ${error?.message}`)));
|
|
2136
|
+
if (test.annotations().length) {
|
|
2137
|
+
this.log();
|
|
2138
|
+
this.printAnnotations(test, "log", 3);
|
|
2139
|
+
this.log();
|
|
2400
2140
|
}
|
|
2401
2141
|
}
|
|
2402
2142
|
printTestSuite(testSuite) {
|
|
@@ -2417,9 +2157,7 @@ class VerboseReporter extends DefaultReporter {
|
|
|
2417
2157
|
}
|
|
2418
2158
|
}
|
|
2419
2159
|
function getIndentation(suite, level = 1) {
|
|
2420
|
-
if (suite.suite && !("filepath" in suite.suite))
|
|
2421
|
-
return getIndentation(suite.suite, level + 1);
|
|
2422
|
-
}
|
|
2160
|
+
if (suite.suite && !("filepath" in suite.suite)) return getIndentation(suite.suite, level + 1);
|
|
2423
2161
|
return level;
|
|
2424
2162
|
}
|
|
2425
2163
|
|