vitest 2.2.0-beta.2 → 3.0.0-beta.2
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 +75 -0
- package/dist/browser.d.ts +13 -10
- package/dist/browser.js +2 -2
- package/dist/chunks/{RandomSequencer.BPedXEug.js → RandomSequencer.gisBJ77r.js} +11 -4
- package/dist/chunks/{base.BS0HhLXd.js → base.CUgXReRN.js} +8 -3
- package/dist/chunks/{cac.Cs06pOqp.js → cac.Xzv7eNWw.js} +22 -17
- package/dist/chunks/{cli-api.CB-jIbYQ.js → cli-api.CETCDGgZ.js} +985 -498
- package/dist/chunks/{config.CPguQ7J1.d.ts → config.BTPBhmK5.d.ts} +1 -1
- package/dist/chunks/{coverage.BoMDb1ip.js → coverage.BWeNbfBa.js} +4 -4
- package/dist/chunks/{creator.IIqd8RWT.js → creator.DcAcUhMD.js} +1 -4
- package/dist/chunks/{environment.CT0jpO-1.d.ts → environment.d8YfPkTm.d.ts} +1 -3
- package/dist/chunks/{globals.BCGEw6ON.js → globals.BFncSRNA.js} +2 -2
- package/dist/chunks/{index.bzFpKeaq.js → index.9ZEBV_TJ.js} +992 -577
- package/dist/chunks/{index.DD5eTY2y.js → index.CkWmZCXU.js} +1 -1
- package/dist/chunks/{index.CqYx2Nsr.js → index.DQboAxJm.js} +23 -14
- package/dist/chunks/{index.BjjsHdBb.js → index.DoV7W5gc.js} +2 -2
- package/dist/chunks/{inspector.70d6emsh.js → inspector.DKLceBVD.js} +1 -1
- package/dist/chunks/{reporters.F9D2idOT.d.ts → reporters.DTtxC3KQ.d.ts} +588 -524
- package/dist/chunks/{resolveConfig.CLnvCvEs.js → resolveConfig.BA-_OKEx.js} +5599 -5570
- package/dist/chunks/{runBaseTests.B7hcVT-s.js → runBaseTests.D0dWpzZV.js} +13 -12
- package/dist/chunks/{setup-common.BfGt8K-K.js → setup-common.Cp_bu5q3.js} +1 -1
- package/dist/chunks/types.BOjykUpq.d.ts +27 -0
- package/dist/chunks/{utils.DJONn5B5.js → utils.CMUTX-p8.js} +5 -2
- package/dist/chunks/{vi.BlPttogV.js → vi.S4Fq8wSo.js} +20 -14
- package/dist/chunks/{vite.DonA4fvH.d.ts → vite.CXaetSK3.d.ts} +1 -1
- package/dist/chunks/{vm.Zr4qWzDJ.js → vm.DGhTouO3.js} +10 -1
- package/dist/chunks/{worker.Qz1UB4Fv.d.ts → worker.ClntunZp.d.ts} +1 -1
- package/dist/chunks/{worker.9VY11NZs.d.ts → worker.o1PBoDdo.d.ts} +4 -4
- package/dist/cli.js +1 -1
- package/dist/config.cjs +1 -10
- package/dist/config.d.ts +7 -10
- package/dist/config.js +1 -10
- package/dist/coverage.d.ts +5 -7
- package/dist/coverage.js +4 -4
- package/dist/environments.d.ts +2 -2
- package/dist/execute.d.ts +4 -4
- package/dist/index.d.ts +27 -22
- package/dist/index.js +2 -2
- package/dist/node.d.ts +25 -33
- package/dist/node.js +66 -32
- package/dist/reporters.d.ts +5 -7
- package/dist/reporters.js +5 -4
- package/dist/runners.d.ts +3 -4
- package/dist/runners.js +9 -14
- package/dist/suite.d.ts +1 -1
- package/dist/worker.js +1 -1
- package/dist/workers/forks.js +1 -1
- package/dist/workers/runVmTests.js +9 -9
- package/dist/workers/threads.js +1 -1
- package/dist/workers/vmForks.js +1 -1
- package/dist/workers/vmThreads.js +1 -1
- package/dist/workers.d.ts +4 -4
- package/dist/workers.js +3 -3
- package/package.json +23 -24
|
@@ -3,367 +3,23 @@ import { getTests, getTestName, hasFailed, getFullName, getSuites, getTasks } fr
|
|
|
3
3
|
import * as pathe from 'pathe';
|
|
4
4
|
import { extname, relative, normalize, resolve, dirname } from 'pathe';
|
|
5
5
|
import c from 'tinyrainbow';
|
|
6
|
-
import { d as divider, F as F_POINTER, w as withLabel, f as formatProjectName, a as formatTimeString, g as getStateSymbol, t as taskFail, b as F_RIGHT, c as F_CHECK, r as renderSnapshotSummary, p as padSummaryTitle, e as getStateString$1, h as formatTime, i as countTestErrors, j as F_TREE_NODE_END, k as F_TREE_NODE_MIDDLE, l as getCols } from './utils.
|
|
6
|
+
import { d as divider, F as F_POINTER, w as withLabel, f as formatProjectName, a as formatTimeString, g as getStateSymbol, t as taskFail, b as F_RIGHT, c as F_CHECK, r as renderSnapshotSummary, p as padSummaryTitle, e as getStateString$1, h as formatTime, i as countTestErrors, j as F_TREE_NODE_END, k as F_TREE_NODE_MIDDLE, l as getCols } from './utils.CMUTX-p8.js';
|
|
7
7
|
import { stripVTControlCharacters } from 'node:util';
|
|
8
8
|
import { highlight, isPrimitive, inspect, positionToOffset, lineSplitRE, toArray, notNullish } from '@vitest/utils';
|
|
9
9
|
import { performance as performance$1 } from 'node:perf_hooks';
|
|
10
10
|
import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
|
|
11
|
-
import { a as TypeCheckError, R as RandomSequencer, g as getOutputFile, b as isNode, c as isDeno } from './RandomSequencer.
|
|
11
|
+
import { a as TypeCheckError, R as RandomSequencer, g as getOutputFile, b as isNode, c as isDeno } from './RandomSequencer.gisBJ77r.js';
|
|
12
12
|
import { isCI } from 'std-env';
|
|
13
13
|
import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
|
|
14
|
-
import restoreCursor from 'restore-cursor';
|
|
15
14
|
import { Writable } from 'node:stream';
|
|
16
15
|
import { Console } from 'node:console';
|
|
17
16
|
import process$1 from 'node:process';
|
|
18
|
-
import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
|
|
17
|
+
import { g as getDefaultExportFromCjs, c as commonjsGlobal } from './_commonjsHelpers.BFTU3MAI.js';
|
|
18
|
+
import require$$0 from 'assert';
|
|
19
|
+
import require$$0$1 from 'events';
|
|
19
20
|
import { createRequire } from 'node:module';
|
|
20
21
|
import { hostname } from 'node:os';
|
|
21
22
|
|
|
22
|
-
class ReportedTaskImplementation {
|
|
23
|
-
/**
|
|
24
|
-
* Task instance.
|
|
25
|
-
* @experimental Public runner task API is experimental and does not follow semver.
|
|
26
|
-
*/
|
|
27
|
-
task;
|
|
28
|
-
/**
|
|
29
|
-
* The project assosiacted with the test or suite.
|
|
30
|
-
*/
|
|
31
|
-
project;
|
|
32
|
-
/**
|
|
33
|
-
* Unique identifier.
|
|
34
|
-
* This ID is deterministic and will be the same for the same test across multiple runs.
|
|
35
|
-
* The ID is based on the project name, module url and test position.
|
|
36
|
-
*/
|
|
37
|
-
id;
|
|
38
|
-
/**
|
|
39
|
-
* Location in the module where the test or suite is defined.
|
|
40
|
-
*/
|
|
41
|
-
location;
|
|
42
|
-
constructor(task, project) {
|
|
43
|
-
this.task = task;
|
|
44
|
-
this.project = project;
|
|
45
|
-
this.id = task.id;
|
|
46
|
-
this.location = task.location;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Creates a new reported task instance and stores it in the project's state for future use.
|
|
50
|
-
*/
|
|
51
|
-
static register(task, project) {
|
|
52
|
-
const state = new this(task, project);
|
|
53
|
-
storeTask(project, task, state);
|
|
54
|
-
return state;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
class TestCase extends ReportedTaskImplementation {
|
|
58
|
-
#fullName;
|
|
59
|
-
type = "test";
|
|
60
|
-
/**
|
|
61
|
-
* Direct reference to the test module where the test or suite is defined.
|
|
62
|
-
*/
|
|
63
|
-
module;
|
|
64
|
-
/**
|
|
65
|
-
* Name of the test.
|
|
66
|
-
*/
|
|
67
|
-
name;
|
|
68
|
-
/**
|
|
69
|
-
* Options that the test was initiated with.
|
|
70
|
-
*/
|
|
71
|
-
options;
|
|
72
|
-
/**
|
|
73
|
-
* Parent suite. If the test was called directly inside the module, the parent will be the module itself.
|
|
74
|
-
*/
|
|
75
|
-
parent;
|
|
76
|
-
constructor(task, project) {
|
|
77
|
-
super(task, project);
|
|
78
|
-
this.name = task.name;
|
|
79
|
-
this.module = getReportedTask(project, task.file);
|
|
80
|
-
const suite = this.task.suite;
|
|
81
|
-
if (suite) {
|
|
82
|
-
this.parent = getReportedTask(project, suite);
|
|
83
|
-
} else {
|
|
84
|
-
this.parent = this.module;
|
|
85
|
-
}
|
|
86
|
-
this.options = buildOptions(task);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Full name of the test including all parent suites separated with `>`.
|
|
90
|
-
*/
|
|
91
|
-
get fullName() {
|
|
92
|
-
if (this.#fullName === void 0) {
|
|
93
|
-
if (this.parent.type !== "module") {
|
|
94
|
-
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
95
|
-
} else {
|
|
96
|
-
this.#fullName = this.name;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return this.#fullName;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Test results. Will be `undefined` if test is not finished yet or was just collected.
|
|
103
|
-
*/
|
|
104
|
-
result() {
|
|
105
|
-
const result = this.task.result;
|
|
106
|
-
if (!result || result.state === "run") {
|
|
107
|
-
return void 0;
|
|
108
|
-
}
|
|
109
|
-
const state = result.state === "fail" ? "failed" : result.state === "pass" ? "passed" : "skipped";
|
|
110
|
-
if (state === "skipped") {
|
|
111
|
-
return {
|
|
112
|
-
state,
|
|
113
|
-
note: result.note,
|
|
114
|
-
errors: void 0
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
if (state === "passed") {
|
|
118
|
-
return {
|
|
119
|
-
state,
|
|
120
|
-
errors: result.errors
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
state,
|
|
125
|
-
errors: result.errors || []
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Checks if the test did not fail the suite.
|
|
130
|
-
* If the test is not finished yet or was skipped, it will return `true`.
|
|
131
|
-
*/
|
|
132
|
-
ok() {
|
|
133
|
-
const result = this.result();
|
|
134
|
-
return !result || result.state !== "failed";
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Custom metadata that was attached to the test during its execution.
|
|
138
|
-
*/
|
|
139
|
-
meta() {
|
|
140
|
-
return this.task.meta;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Useful information about the test like duration, memory usage, etc.
|
|
144
|
-
* Diagnostic is only available after the test has finished.
|
|
145
|
-
*/
|
|
146
|
-
diagnostic() {
|
|
147
|
-
const result = this.task.result;
|
|
148
|
-
if (!result || result.state === "run" || !result.startTime) {
|
|
149
|
-
return void 0;
|
|
150
|
-
}
|
|
151
|
-
const duration = result.duration || 0;
|
|
152
|
-
const slow = duration > this.project.globalConfig.slowTestThreshold;
|
|
153
|
-
return {
|
|
154
|
-
slow,
|
|
155
|
-
heap: result.heap,
|
|
156
|
-
duration,
|
|
157
|
-
startTime: result.startTime,
|
|
158
|
-
retryCount: result.retryCount ?? 0,
|
|
159
|
-
repeatCount: result.repeatCount ?? 0,
|
|
160
|
-
flaky: !!result.retryCount && result.state === "pass" && result.retryCount > 0
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
class TestCollection {
|
|
165
|
-
#task;
|
|
166
|
-
#project;
|
|
167
|
-
constructor(task, project) {
|
|
168
|
-
this.#task = task;
|
|
169
|
-
this.#project = project;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Returns the test or suite at a specific index in the array.
|
|
173
|
-
*/
|
|
174
|
-
at(index) {
|
|
175
|
-
if (index < 0) {
|
|
176
|
-
index = this.size + index;
|
|
177
|
-
}
|
|
178
|
-
return getReportedTask(this.#project, this.#task.tasks[index]);
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* The number of tests and suites in the collection.
|
|
182
|
-
*/
|
|
183
|
-
get size() {
|
|
184
|
-
return this.#task.tasks.length;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Returns the collection in array form for easier manipulation.
|
|
188
|
-
*/
|
|
189
|
-
array() {
|
|
190
|
-
return Array.from(this);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Filters all tests that are part of this collection and its children.
|
|
194
|
-
*/
|
|
195
|
-
*allTests(state) {
|
|
196
|
-
for (const child of this) {
|
|
197
|
-
if (child.type === "suite") {
|
|
198
|
-
yield* child.children.allTests(state);
|
|
199
|
-
} else if (state) {
|
|
200
|
-
const testState = getTestState(child);
|
|
201
|
-
if (state === testState) {
|
|
202
|
-
yield child;
|
|
203
|
-
}
|
|
204
|
-
} else {
|
|
205
|
-
yield child;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Filters only the tests that are part of this collection.
|
|
211
|
-
*/
|
|
212
|
-
*tests(state) {
|
|
213
|
-
for (const child of this) {
|
|
214
|
-
if (child.type !== "test") {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (state) {
|
|
218
|
-
const testState = getTestState(child);
|
|
219
|
-
if (state === testState) {
|
|
220
|
-
yield child;
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
yield child;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Filters only the suites that are part of this collection.
|
|
229
|
-
*/
|
|
230
|
-
*suites() {
|
|
231
|
-
for (const child of this) {
|
|
232
|
-
if (child.type === "suite") {
|
|
233
|
-
yield child;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Filters all suites that are part of this collection and its children.
|
|
239
|
-
*/
|
|
240
|
-
*allSuites() {
|
|
241
|
-
for (const child of this) {
|
|
242
|
-
if (child.type === "suite") {
|
|
243
|
-
yield child;
|
|
244
|
-
yield* child.children.allSuites();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
*[Symbol.iterator]() {
|
|
249
|
-
for (const task of this.#task.tasks) {
|
|
250
|
-
yield getReportedTask(this.#project, task);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
class SuiteImplementation extends ReportedTaskImplementation {
|
|
255
|
-
/**
|
|
256
|
-
* Collection of suites and tests that are part of this suite.
|
|
257
|
-
*/
|
|
258
|
-
children;
|
|
259
|
-
constructor(task, project) {
|
|
260
|
-
super(task, project);
|
|
261
|
-
this.children = new TestCollection(task, project);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
class TestSuite extends SuiteImplementation {
|
|
265
|
-
#fullName;
|
|
266
|
-
type = "suite";
|
|
267
|
-
/**
|
|
268
|
-
* Name of the test or the suite.
|
|
269
|
-
*/
|
|
270
|
-
name;
|
|
271
|
-
/**
|
|
272
|
-
* Direct reference to the test module where the test or suite is defined.
|
|
273
|
-
*/
|
|
274
|
-
module;
|
|
275
|
-
/**
|
|
276
|
-
* Parent suite. If suite was called directly inside the module, the parent will be the module itself.
|
|
277
|
-
*/
|
|
278
|
-
parent;
|
|
279
|
-
/**
|
|
280
|
-
* Options that suite was initiated with.
|
|
281
|
-
*/
|
|
282
|
-
options;
|
|
283
|
-
constructor(task, project) {
|
|
284
|
-
super(task, project);
|
|
285
|
-
this.name = task.name;
|
|
286
|
-
this.module = getReportedTask(project, task.file);
|
|
287
|
-
const suite = this.task.suite;
|
|
288
|
-
if (suite) {
|
|
289
|
-
this.parent = getReportedTask(project, suite);
|
|
290
|
-
} else {
|
|
291
|
-
this.parent = this.module;
|
|
292
|
-
}
|
|
293
|
-
this.options = buildOptions(task);
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Full name of the suite including all parent suites separated with `>`.
|
|
297
|
-
*/
|
|
298
|
-
get fullName() {
|
|
299
|
-
if (this.#fullName === void 0) {
|
|
300
|
-
if (this.parent.type !== "module") {
|
|
301
|
-
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
302
|
-
} else {
|
|
303
|
-
this.#fullName = this.name;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
return this.#fullName;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
class TestModule extends SuiteImplementation {
|
|
310
|
-
type = "module";
|
|
311
|
-
/**
|
|
312
|
-
* This is usually an absolute UNIX file path.
|
|
313
|
-
* It can be a virtual id if the file is not on the disk.
|
|
314
|
-
* This value corresponds to Vite's `ModuleGraph` id.
|
|
315
|
-
*/
|
|
316
|
-
moduleId;
|
|
317
|
-
constructor(task, project) {
|
|
318
|
-
super(task, project);
|
|
319
|
-
this.moduleId = task.filepath;
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Useful information about the module like duration, memory usage, etc.
|
|
323
|
-
* If the module was not executed yet, all diagnostic values will return `0`.
|
|
324
|
-
*/
|
|
325
|
-
diagnostic() {
|
|
326
|
-
const setupDuration = this.task.setupDuration || 0;
|
|
327
|
-
const collectDuration = this.task.collectDuration || 0;
|
|
328
|
-
const prepareDuration = this.task.prepareDuration || 0;
|
|
329
|
-
const environmentSetupDuration = this.task.environmentLoad || 0;
|
|
330
|
-
const duration = this.task.result?.duration || 0;
|
|
331
|
-
return {
|
|
332
|
-
environmentSetupDuration,
|
|
333
|
-
prepareDuration,
|
|
334
|
-
collectDuration,
|
|
335
|
-
setupDuration,
|
|
336
|
-
duration
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
function buildOptions(task) {
|
|
341
|
-
return {
|
|
342
|
-
each: task.each,
|
|
343
|
-
concurrent: task.concurrent,
|
|
344
|
-
shuffle: task.shuffle,
|
|
345
|
-
retry: task.retry,
|
|
346
|
-
repeats: task.repeats,
|
|
347
|
-
mode: task.mode
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
function getTestState(test) {
|
|
351
|
-
const result = test.result();
|
|
352
|
-
return result ? result.state : "running";
|
|
353
|
-
}
|
|
354
|
-
function storeTask(project, runnerTask, reportedTask) {
|
|
355
|
-
project.vitest.state.reportedTasksMap.set(runnerTask, reportedTask);
|
|
356
|
-
}
|
|
357
|
-
function getReportedTask(project, runnerTask) {
|
|
358
|
-
const reportedTask = project.vitest.state.getReportedEntity(runnerTask);
|
|
359
|
-
if (!reportedTask) {
|
|
360
|
-
throw new Error(
|
|
361
|
-
`Task instance was not found for ${runnerTask.type} "${runnerTask.name}"`
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
return reportedTask;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
23
|
/// <reference types="../types/index.d.ts" />
|
|
368
24
|
|
|
369
25
|
// (c) 2020-present Andrea Giammarchi
|
|
@@ -626,6 +282,370 @@ ansiEscapes.iTerm = {
|
|
|
626
282
|
}
|
|
627
283
|
};
|
|
628
284
|
|
|
285
|
+
var onetime$1 = {exports: {}};
|
|
286
|
+
|
|
287
|
+
var mimicFn = {exports: {}};
|
|
288
|
+
|
|
289
|
+
var hasRequiredMimicFn;
|
|
290
|
+
|
|
291
|
+
function requireMimicFn () {
|
|
292
|
+
if (hasRequiredMimicFn) return mimicFn.exports;
|
|
293
|
+
hasRequiredMimicFn = 1;
|
|
294
|
+
|
|
295
|
+
const mimicFn$1 = (to, from) => {
|
|
296
|
+
for (const prop of Reflect.ownKeys(from)) {
|
|
297
|
+
Object.defineProperty(to, prop, Object.getOwnPropertyDescriptor(from, prop));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return to;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
mimicFn.exports = mimicFn$1;
|
|
304
|
+
// TODO: Remove this for the next major release
|
|
305
|
+
mimicFn.exports.default = mimicFn$1;
|
|
306
|
+
return mimicFn.exports;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
var hasRequiredOnetime;
|
|
310
|
+
|
|
311
|
+
function requireOnetime () {
|
|
312
|
+
if (hasRequiredOnetime) return onetime$1.exports;
|
|
313
|
+
hasRequiredOnetime = 1;
|
|
314
|
+
const mimicFn = requireMimicFn();
|
|
315
|
+
|
|
316
|
+
const calledFunctions = new WeakMap();
|
|
317
|
+
|
|
318
|
+
const onetime = (function_, options = {}) => {
|
|
319
|
+
if (typeof function_ !== 'function') {
|
|
320
|
+
throw new TypeError('Expected a function');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let returnValue;
|
|
324
|
+
let callCount = 0;
|
|
325
|
+
const functionName = function_.displayName || function_.name || '<anonymous>';
|
|
326
|
+
|
|
327
|
+
const onetime = function (...arguments_) {
|
|
328
|
+
calledFunctions.set(onetime, ++callCount);
|
|
329
|
+
|
|
330
|
+
if (callCount === 1) {
|
|
331
|
+
returnValue = function_.apply(this, arguments_);
|
|
332
|
+
function_ = null;
|
|
333
|
+
} else if (options.throw === true) {
|
|
334
|
+
throw new Error(`Function \`${functionName}\` can only be called once`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return returnValue;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
mimicFn(onetime, function_);
|
|
341
|
+
calledFunctions.set(onetime, callCount);
|
|
342
|
+
|
|
343
|
+
return onetime;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
onetime$1.exports = onetime;
|
|
347
|
+
// TODO: Remove this for the next major release
|
|
348
|
+
onetime$1.exports.default = onetime;
|
|
349
|
+
|
|
350
|
+
onetime$1.exports.callCount = function_ => {
|
|
351
|
+
if (!calledFunctions.has(function_)) {
|
|
352
|
+
throw new Error(`The given function \`${function_.name}\` is not wrapped by the \`onetime\` package`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return calledFunctions.get(function_);
|
|
356
|
+
};
|
|
357
|
+
return onetime$1.exports;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
var onetimeExports = requireOnetime();
|
|
361
|
+
var onetime = /*@__PURE__*/getDefaultExportFromCjs(onetimeExports);
|
|
362
|
+
|
|
363
|
+
var signalExit$1 = {exports: {}};
|
|
364
|
+
|
|
365
|
+
var signals = {exports: {}};
|
|
366
|
+
|
|
367
|
+
var hasRequiredSignals;
|
|
368
|
+
|
|
369
|
+
function requireSignals () {
|
|
370
|
+
if (hasRequiredSignals) return signals.exports;
|
|
371
|
+
hasRequiredSignals = 1;
|
|
372
|
+
(function (module) {
|
|
373
|
+
// This is not the set of all possible signals.
|
|
374
|
+
//
|
|
375
|
+
// It IS, however, the set of all signals that trigger
|
|
376
|
+
// an exit on either Linux or BSD systems. Linux is a
|
|
377
|
+
// superset of the signal names supported on BSD, and
|
|
378
|
+
// the unknown signals just fail to register, so we can
|
|
379
|
+
// catch that easily enough.
|
|
380
|
+
//
|
|
381
|
+
// Don't bother with SIGKILL. It's uncatchable, which
|
|
382
|
+
// means that we can't fire any callbacks anyway.
|
|
383
|
+
//
|
|
384
|
+
// If a user does happen to register a handler on a non-
|
|
385
|
+
// fatal signal like SIGWINCH or something, and then
|
|
386
|
+
// exit, it'll end up firing `process.emit('exit')`, so
|
|
387
|
+
// the handler will be fired anyway.
|
|
388
|
+
//
|
|
389
|
+
// SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
|
|
390
|
+
// artificially, inherently leave the process in a
|
|
391
|
+
// state from which it is not safe to try and enter JS
|
|
392
|
+
// listeners.
|
|
393
|
+
module.exports = [
|
|
394
|
+
'SIGABRT',
|
|
395
|
+
'SIGALRM',
|
|
396
|
+
'SIGHUP',
|
|
397
|
+
'SIGINT',
|
|
398
|
+
'SIGTERM'
|
|
399
|
+
];
|
|
400
|
+
|
|
401
|
+
if (process.platform !== 'win32') {
|
|
402
|
+
module.exports.push(
|
|
403
|
+
'SIGVTALRM',
|
|
404
|
+
'SIGXCPU',
|
|
405
|
+
'SIGXFSZ',
|
|
406
|
+
'SIGUSR2',
|
|
407
|
+
'SIGTRAP',
|
|
408
|
+
'SIGSYS',
|
|
409
|
+
'SIGQUIT',
|
|
410
|
+
'SIGIOT'
|
|
411
|
+
// should detect profiler and enable/disable accordingly.
|
|
412
|
+
// see #21
|
|
413
|
+
// 'SIGPROF'
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (process.platform === 'linux') {
|
|
418
|
+
module.exports.push(
|
|
419
|
+
'SIGIO',
|
|
420
|
+
'SIGPOLL',
|
|
421
|
+
'SIGPWR',
|
|
422
|
+
'SIGSTKFLT',
|
|
423
|
+
'SIGUNUSED'
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
} (signals));
|
|
427
|
+
return signals.exports;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
var hasRequiredSignalExit;
|
|
431
|
+
|
|
432
|
+
function requireSignalExit () {
|
|
433
|
+
if (hasRequiredSignalExit) return signalExit$1.exports;
|
|
434
|
+
hasRequiredSignalExit = 1;
|
|
435
|
+
// Note: since nyc uses this module to output coverage, any lines
|
|
436
|
+
// that are in the direct sync flow of nyc's outputCoverage are
|
|
437
|
+
// ignored, since we can never get coverage for them.
|
|
438
|
+
// grab a reference to node's real process object right away
|
|
439
|
+
var process = commonjsGlobal.process;
|
|
440
|
+
|
|
441
|
+
const processOk = function (process) {
|
|
442
|
+
return process &&
|
|
443
|
+
typeof process === 'object' &&
|
|
444
|
+
typeof process.removeListener === 'function' &&
|
|
445
|
+
typeof process.emit === 'function' &&
|
|
446
|
+
typeof process.reallyExit === 'function' &&
|
|
447
|
+
typeof process.listeners === 'function' &&
|
|
448
|
+
typeof process.kill === 'function' &&
|
|
449
|
+
typeof process.pid === 'number' &&
|
|
450
|
+
typeof process.on === 'function'
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// some kind of non-node environment, just no-op
|
|
454
|
+
/* istanbul ignore if */
|
|
455
|
+
if (!processOk(process)) {
|
|
456
|
+
signalExit$1.exports = function () {
|
|
457
|
+
return function () {}
|
|
458
|
+
};
|
|
459
|
+
} else {
|
|
460
|
+
var assert = require$$0;
|
|
461
|
+
var signals = requireSignals();
|
|
462
|
+
var isWin = /^win/i.test(process.platform);
|
|
463
|
+
|
|
464
|
+
var EE = require$$0$1;
|
|
465
|
+
/* istanbul ignore if */
|
|
466
|
+
if (typeof EE !== 'function') {
|
|
467
|
+
EE = EE.EventEmitter;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
var emitter;
|
|
471
|
+
if (process.__signal_exit_emitter__) {
|
|
472
|
+
emitter = process.__signal_exit_emitter__;
|
|
473
|
+
} else {
|
|
474
|
+
emitter = process.__signal_exit_emitter__ = new EE();
|
|
475
|
+
emitter.count = 0;
|
|
476
|
+
emitter.emitted = {};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Because this emitter is a global, we have to check to see if a
|
|
480
|
+
// previous version of this library failed to enable infinite listeners.
|
|
481
|
+
// I know what you're about to say. But literally everything about
|
|
482
|
+
// signal-exit is a compromise with evil. Get used to it.
|
|
483
|
+
if (!emitter.infinite) {
|
|
484
|
+
emitter.setMaxListeners(Infinity);
|
|
485
|
+
emitter.infinite = true;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
signalExit$1.exports = function (cb, opts) {
|
|
489
|
+
/* istanbul ignore if */
|
|
490
|
+
if (!processOk(commonjsGlobal.process)) {
|
|
491
|
+
return function () {}
|
|
492
|
+
}
|
|
493
|
+
assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler');
|
|
494
|
+
|
|
495
|
+
if (loaded === false) {
|
|
496
|
+
load();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
var ev = 'exit';
|
|
500
|
+
if (opts && opts.alwaysLast) {
|
|
501
|
+
ev = 'afterexit';
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
var remove = function () {
|
|
505
|
+
emitter.removeListener(ev, cb);
|
|
506
|
+
if (emitter.listeners('exit').length === 0 &&
|
|
507
|
+
emitter.listeners('afterexit').length === 0) {
|
|
508
|
+
unload();
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
emitter.on(ev, cb);
|
|
512
|
+
|
|
513
|
+
return remove
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
var unload = function unload () {
|
|
517
|
+
if (!loaded || !processOk(commonjsGlobal.process)) {
|
|
518
|
+
return
|
|
519
|
+
}
|
|
520
|
+
loaded = false;
|
|
521
|
+
|
|
522
|
+
signals.forEach(function (sig) {
|
|
523
|
+
try {
|
|
524
|
+
process.removeListener(sig, sigListeners[sig]);
|
|
525
|
+
} catch (er) {}
|
|
526
|
+
});
|
|
527
|
+
process.emit = originalProcessEmit;
|
|
528
|
+
process.reallyExit = originalProcessReallyExit;
|
|
529
|
+
emitter.count -= 1;
|
|
530
|
+
};
|
|
531
|
+
signalExit$1.exports.unload = unload;
|
|
532
|
+
|
|
533
|
+
var emit = function emit (event, code, signal) {
|
|
534
|
+
/* istanbul ignore if */
|
|
535
|
+
if (emitter.emitted[event]) {
|
|
536
|
+
return
|
|
537
|
+
}
|
|
538
|
+
emitter.emitted[event] = true;
|
|
539
|
+
emitter.emit(event, code, signal);
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// { <signal>: <listener fn>, ... }
|
|
543
|
+
var sigListeners = {};
|
|
544
|
+
signals.forEach(function (sig) {
|
|
545
|
+
sigListeners[sig] = function listener () {
|
|
546
|
+
/* istanbul ignore if */
|
|
547
|
+
if (!processOk(commonjsGlobal.process)) {
|
|
548
|
+
return
|
|
549
|
+
}
|
|
550
|
+
// If there are no other listeners, an exit is coming!
|
|
551
|
+
// Simplest way: remove us and then re-send the signal.
|
|
552
|
+
// We know that this will kill the process, so we can
|
|
553
|
+
// safely emit now.
|
|
554
|
+
var listeners = process.listeners(sig);
|
|
555
|
+
if (listeners.length === emitter.count) {
|
|
556
|
+
unload();
|
|
557
|
+
emit('exit', null, sig);
|
|
558
|
+
/* istanbul ignore next */
|
|
559
|
+
emit('afterexit', null, sig);
|
|
560
|
+
/* istanbul ignore next */
|
|
561
|
+
if (isWin && sig === 'SIGHUP') {
|
|
562
|
+
// "SIGHUP" throws an `ENOSYS` error on Windows,
|
|
563
|
+
// so use a supported signal instead
|
|
564
|
+
sig = 'SIGINT';
|
|
565
|
+
}
|
|
566
|
+
/* istanbul ignore next */
|
|
567
|
+
process.kill(process.pid, sig);
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
signalExit$1.exports.signals = function () {
|
|
573
|
+
return signals
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
var loaded = false;
|
|
577
|
+
|
|
578
|
+
var load = function load () {
|
|
579
|
+
if (loaded || !processOk(commonjsGlobal.process)) {
|
|
580
|
+
return
|
|
581
|
+
}
|
|
582
|
+
loaded = true;
|
|
583
|
+
|
|
584
|
+
// This is the number of onSignalExit's that are in play.
|
|
585
|
+
// It's important so that we can count the correct number of
|
|
586
|
+
// listeners on signals, and don't wait for the other one to
|
|
587
|
+
// handle it instead of us.
|
|
588
|
+
emitter.count += 1;
|
|
589
|
+
|
|
590
|
+
signals = signals.filter(function (sig) {
|
|
591
|
+
try {
|
|
592
|
+
process.on(sig, sigListeners[sig]);
|
|
593
|
+
return true
|
|
594
|
+
} catch (er) {
|
|
595
|
+
return false
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
process.emit = processEmit;
|
|
600
|
+
process.reallyExit = processReallyExit;
|
|
601
|
+
};
|
|
602
|
+
signalExit$1.exports.load = load;
|
|
603
|
+
|
|
604
|
+
var originalProcessReallyExit = process.reallyExit;
|
|
605
|
+
var processReallyExit = function processReallyExit (code) {
|
|
606
|
+
/* istanbul ignore if */
|
|
607
|
+
if (!processOk(commonjsGlobal.process)) {
|
|
608
|
+
return
|
|
609
|
+
}
|
|
610
|
+
process.exitCode = code || /* istanbul ignore next */ 0;
|
|
611
|
+
emit('exit', process.exitCode, null);
|
|
612
|
+
/* istanbul ignore next */
|
|
613
|
+
emit('afterexit', process.exitCode, null);
|
|
614
|
+
/* istanbul ignore next */
|
|
615
|
+
originalProcessReallyExit.call(process, process.exitCode);
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
var originalProcessEmit = process.emit;
|
|
619
|
+
var processEmit = function processEmit (ev, arg) {
|
|
620
|
+
if (ev === 'exit' && processOk(commonjsGlobal.process)) {
|
|
621
|
+
/* istanbul ignore else */
|
|
622
|
+
if (arg !== undefined) {
|
|
623
|
+
process.exitCode = arg;
|
|
624
|
+
}
|
|
625
|
+
var ret = originalProcessEmit.apply(this, arguments);
|
|
626
|
+
/* istanbul ignore next */
|
|
627
|
+
emit('exit', process.exitCode, null);
|
|
628
|
+
/* istanbul ignore next */
|
|
629
|
+
emit('afterexit', process.exitCode, null);
|
|
630
|
+
/* istanbul ignore next */
|
|
631
|
+
return ret
|
|
632
|
+
} else {
|
|
633
|
+
return originalProcessEmit.apply(this, arguments)
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
return signalExit$1.exports;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
var signalExitExports = requireSignalExit();
|
|
641
|
+
var signalExit = /*@__PURE__*/getDefaultExportFromCjs(signalExitExports);
|
|
642
|
+
|
|
643
|
+
const restoreCursor = onetime(() => {
|
|
644
|
+
signalExit(() => {
|
|
645
|
+
process$1.stderr.write('\u001B[?25h');
|
|
646
|
+
}, {alwaysLast: true});
|
|
647
|
+
});
|
|
648
|
+
|
|
629
649
|
let isHidden = false;
|
|
630
650
|
|
|
631
651
|
const cliCursor = {};
|
|
@@ -2804,15 +2824,15 @@ Vitest caught ${errors.length} unhandled error${errors.length > 1 ? "s" : ""} du
|
|
|
2804
2824
|
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.`
|
|
2805
2825
|
)
|
|
2806
2826
|
);
|
|
2807
|
-
this.
|
|
2808
|
-
this.
|
|
2827
|
+
this.error(c.red(divider(c.bold(c.inverse(" Unhandled Errors ")))));
|
|
2828
|
+
this.error(errorMessage);
|
|
2809
2829
|
errors.forEach((err) => {
|
|
2810
2830
|
this.printError(err, {
|
|
2811
2831
|
fullStack: true,
|
|
2812
2832
|
type: err.type || "Unhandled Error"
|
|
2813
2833
|
});
|
|
2814
2834
|
});
|
|
2815
|
-
this.
|
|
2835
|
+
this.error(c.red(divider()));
|
|
2816
2836
|
}
|
|
2817
2837
|
printSourceTypeErrors(errors) {
|
|
2818
2838
|
const errorMessage = c.red(
|
|
@@ -2866,7 +2886,7 @@ class BlobReporter {
|
|
|
2866
2886
|
const modules = this.ctx.projects.map(
|
|
2867
2887
|
(project) => {
|
|
2868
2888
|
return [
|
|
2869
|
-
project.
|
|
2889
|
+
project.name,
|
|
2870
2890
|
[...project.vite.moduleGraph.idToModuleMap.entries()].map((mod) => {
|
|
2871
2891
|
if (!mod[1].file) {
|
|
2872
2892
|
return null;
|
|
@@ -2934,7 +2954,7 @@ ${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`
|
|
|
2934
2954
|
);
|
|
2935
2955
|
}
|
|
2936
2956
|
const projects = Object.fromEntries(
|
|
2937
|
-
projectsArray.map((p) => [p.
|
|
2957
|
+
projectsArray.map((p) => [p.name, p])
|
|
2938
2958
|
);
|
|
2939
2959
|
blobs.forEach((blob) => {
|
|
2940
2960
|
blob.moduleKeys.forEach(([projectName, moduleIds]) => {
|
|
@@ -3041,21 +3061,27 @@ class BaseReporter {
|
|
|
3041
3061
|
this.log(` ${title} ${task.name} ${suffix}`);
|
|
3042
3062
|
const anyFailed = tests.some((test) => test.result?.state === "fail");
|
|
3043
3063
|
for (const test of tests) {
|
|
3044
|
-
const duration = test.result
|
|
3064
|
+
const { duration, retryCount, repeatCount } = test.result || {};
|
|
3065
|
+
let suffix2 = "";
|
|
3066
|
+
if (retryCount != null && retryCount > 0) {
|
|
3067
|
+
suffix2 += c.yellow(` (retry x${retryCount})`);
|
|
3068
|
+
}
|
|
3069
|
+
if (repeatCount != null && repeatCount > 0) {
|
|
3070
|
+
suffix2 += c.yellow(` (repeat x${repeatCount})`);
|
|
3071
|
+
}
|
|
3045
3072
|
if (test.result?.state === "fail") {
|
|
3046
|
-
|
|
3047
|
-
this.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${suffix2}`));
|
|
3073
|
+
this.log(c.red(` ${taskFail} ${getTestName(test, c.dim(" > "))}${this.getDurationPrefix(test)}`) + suffix2);
|
|
3048
3074
|
test.result?.errors?.forEach((e) => {
|
|
3049
3075
|
this.log(c.red(` ${F_RIGHT} ${e?.message}`));
|
|
3050
3076
|
});
|
|
3051
3077
|
} else if (duration && duration > this.ctx.config.slowTestThreshold) {
|
|
3052
3078
|
this.log(
|
|
3053
|
-
` ${c.yellow(c.dim(F_CHECK))} ${getTestName(test, c.dim(" > "))} ${c.yellow(Math.round(duration) + c.dim("ms"))}`
|
|
3079
|
+
` ${c.yellow(c.dim(F_CHECK))} ${getTestName(test, c.dim(" > "))} ${c.yellow(Math.round(duration) + c.dim("ms"))}${suffix2}`
|
|
3054
3080
|
);
|
|
3055
|
-
} else if (test.result?.state === "skip" && test.result.note) {
|
|
3081
|
+
} else if (this.ctx.config.hideSkippedTests && (test.mode === "skip" || test.result?.state === "skip")) ; else if (test.result?.state === "skip" && test.result.note) {
|
|
3056
3082
|
this.log(` ${getStateSymbol(test)} ${getTestName(test)}${c.dim(c.gray(` [${test.result.note}]`))}`);
|
|
3057
3083
|
} else if (this.renderSucceed || anyFailed) {
|
|
3058
|
-
this.log(` ${c.
|
|
3084
|
+
this.log(` ${c.dim(getStateSymbol(test))} ${getTestName(test, c.dim(" > "))}${suffix2}`);
|
|
3059
3085
|
}
|
|
3060
3086
|
}
|
|
3061
3087
|
}
|
|
@@ -3103,7 +3129,7 @@ class BaseReporter {
|
|
|
3103
3129
|
this.log(BADGE_PADDING + c.dim(" Project name: ") + c.blue(toArray(this.ctx.configOverride.project).join(", ")));
|
|
3104
3130
|
}
|
|
3105
3131
|
if (this.ctx.filenamePattern) {
|
|
3106
|
-
this.log(BADGE_PADDING + c.dim(" Filename pattern: ") + c.blue(this.ctx.filenamePattern));
|
|
3132
|
+
this.log(BADGE_PADDING + c.dim(" Filename pattern: ") + c.blue(this.ctx.filenamePattern.join(", ")));
|
|
3107
3133
|
}
|
|
3108
3134
|
if (this.ctx.configOverride.testNamePattern) {
|
|
3109
3135
|
this.log(BADGE_PADDING + c.dim(" Test name pattern: ") + c.blue(String(this.ctx.configOverride.testNamePattern)));
|
|
@@ -3134,7 +3160,7 @@ class BaseReporter {
|
|
|
3134
3160
|
if (log.browser) {
|
|
3135
3161
|
write("\n");
|
|
3136
3162
|
}
|
|
3137
|
-
const project =
|
|
3163
|
+
const project = task ? this.ctx.getProjectByName(task.file.projectName || "") : this.ctx.getRootProject();
|
|
3138
3164
|
const stack = log.browser ? project.browser?.parseStacktrace(log.origin) || [] : parseStacktrace(log.origin);
|
|
3139
3165
|
const highlight = task && stack.find((i) => i.file === task.file.filepath);
|
|
3140
3166
|
for (const frame of stack) {
|
|
@@ -3245,12 +3271,14 @@ class BaseReporter {
|
|
|
3245
3271
|
const errorDivider = () => this.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1)))}
|
|
3246
3272
|
`);
|
|
3247
3273
|
if (failedSuites.length) {
|
|
3248
|
-
this.error(
|
|
3274
|
+
this.error(`
|
|
3275
|
+
${errorBanner(`Failed Suites ${failedSuites.length}`)}
|
|
3249
3276
|
`);
|
|
3250
3277
|
this.printTaskErrors(failedSuites, errorDivider);
|
|
3251
3278
|
}
|
|
3252
3279
|
if (failedTests.length) {
|
|
3253
|
-
this.error(
|
|
3280
|
+
this.error(`
|
|
3281
|
+
${errorBanner(`Failed Tests ${failedTests.length}`)}
|
|
3254
3282
|
`);
|
|
3255
3283
|
this.printTaskErrors(failedTests, errorDivider);
|
|
3256
3284
|
}
|
|
@@ -3309,12 +3337,12 @@ class BaseReporter {
|
|
|
3309
3337
|
name += c.dim(` [ ${this.relative(filepath)} ]`);
|
|
3310
3338
|
}
|
|
3311
3339
|
this.ctx.logger.error(
|
|
3312
|
-
`${c.red(c.bold(c.inverse(" FAIL ")))}${formatProjectName(projectName)}
|
|
3340
|
+
`${c.red(c.bold(c.inverse(" FAIL ")))} ${formatProjectName(projectName)}${name}`
|
|
3313
3341
|
);
|
|
3314
3342
|
}
|
|
3315
3343
|
const screenshotPaths = tasks2.map((t) => t.meta?.failScreenshotPath).filter((screenshot) => screenshot != null);
|
|
3316
3344
|
this.ctx.logger.printError(error, {
|
|
3317
|
-
project: this.ctx.
|
|
3345
|
+
project: this.ctx.getProjectByName(tasks2[0].file.projectName || ""),
|
|
3318
3346
|
verbose: this.verbose,
|
|
3319
3347
|
screenshotPaths,
|
|
3320
3348
|
task: tasks2[0]
|
|
@@ -3378,9 +3406,9 @@ class WindowRenderer {
|
|
|
3378
3406
|
};
|
|
3379
3407
|
this.cleanups.push(
|
|
3380
3408
|
this.interceptStream(process.stdout, "output"),
|
|
3381
|
-
this.interceptStream(process.stderr, "error")
|
|
3409
|
+
this.interceptStream(process.stderr, "error"),
|
|
3410
|
+
this.addProcessExitListeners()
|
|
3382
3411
|
);
|
|
3383
|
-
restoreCursor();
|
|
3384
3412
|
this.write(HIDE_CURSOR, "output");
|
|
3385
3413
|
this.start();
|
|
3386
3414
|
}
|
|
@@ -3402,6 +3430,9 @@ class WindowRenderer {
|
|
|
3402
3430
|
this.flushBuffer();
|
|
3403
3431
|
clearInterval(this.renderInterval);
|
|
3404
3432
|
}
|
|
3433
|
+
getColumns() {
|
|
3434
|
+
return "columns" in this.options.logger.outputStream ? this.options.logger.outputStream.columns : 80;
|
|
3435
|
+
}
|
|
3405
3436
|
flushBuffer() {
|
|
3406
3437
|
if (this.buffer.length === 0) {
|
|
3407
3438
|
return this.render();
|
|
@@ -3429,10 +3460,10 @@ class WindowRenderer {
|
|
|
3429
3460
|
return this.write(message || "", type);
|
|
3430
3461
|
}
|
|
3431
3462
|
const windowContent = this.options.getWindow();
|
|
3432
|
-
const rowCount = getRenderedRowCount(windowContent, this.
|
|
3463
|
+
const rowCount = getRenderedRowCount(windowContent, this.getColumns());
|
|
3433
3464
|
let padding = this.windowHeight - rowCount;
|
|
3434
3465
|
if (padding > 0 && message) {
|
|
3435
|
-
padding -= getRenderedRowCount([message], this.
|
|
3466
|
+
padding -= getRenderedRowCount([message], this.getColumns());
|
|
3436
3467
|
}
|
|
3437
3468
|
this.write(SYNC_START);
|
|
3438
3469
|
this.clearWindow();
|
|
@@ -3475,10 +3506,27 @@ class WindowRenderer {
|
|
|
3475
3506
|
write(message, type = "output") {
|
|
3476
3507
|
this.streams[type](message);
|
|
3477
3508
|
}
|
|
3509
|
+
addProcessExitListeners() {
|
|
3510
|
+
const onExit = (signal, exitCode) => {
|
|
3511
|
+
this.flushBuffer();
|
|
3512
|
+
this.stop();
|
|
3513
|
+
if (process.exitCode === void 0) {
|
|
3514
|
+
process.exitCode = exitCode !== void 0 ? 128 + exitCode : Number(signal);
|
|
3515
|
+
}
|
|
3516
|
+
process.exit();
|
|
3517
|
+
};
|
|
3518
|
+
process.once("SIGINT", onExit);
|
|
3519
|
+
process.once("SIGTERM", onExit);
|
|
3520
|
+
process.once("exit", onExit);
|
|
3521
|
+
return function cleanup() {
|
|
3522
|
+
process.off("SIGINT", onExit);
|
|
3523
|
+
process.off("SIGTERM", onExit);
|
|
3524
|
+
process.off("exit", onExit);
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3478
3527
|
}
|
|
3479
|
-
function getRenderedRowCount(rows,
|
|
3528
|
+
function getRenderedRowCount(rows, columns) {
|
|
3480
3529
|
let count = 0;
|
|
3481
|
-
const columns = "columns" in stream ? stream.columns : 80;
|
|
3482
3530
|
for (const row of rows) {
|
|
3483
3531
|
const text = stripVTControlCharacters(row);
|
|
3484
3532
|
count += Math.max(1, Math.ceil(text.length / columns));
|
|
@@ -3524,7 +3572,7 @@ class TaskParser {
|
|
|
3524
3572
|
finishedTestFiles.push(task.file);
|
|
3525
3573
|
}
|
|
3526
3574
|
}
|
|
3527
|
-
if (task?.type === "test"
|
|
3575
|
+
if (task?.type === "test") {
|
|
3528
3576
|
if (task.result?.state === "run") {
|
|
3529
3577
|
startingTests.push(task);
|
|
3530
3578
|
} else if (task.result?.hooks?.afterEach !== "run") {
|
|
@@ -3855,139 +3903,131 @@ class DefaultReporter extends BaseReporter {
|
|
|
3855
3903
|
}
|
|
3856
3904
|
}
|
|
3857
3905
|
|
|
3858
|
-
|
|
3859
|
-
|
|
3906
|
+
class DotReporter extends BaseReporter {
|
|
3907
|
+
summary;
|
|
3908
|
+
onInit(ctx) {
|
|
3909
|
+
super.onInit(ctx);
|
|
3910
|
+
if (this.isTTY) {
|
|
3911
|
+
this.summary = new DotSummary();
|
|
3912
|
+
this.summary.onInit(ctx);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
onTaskUpdate(packs) {
|
|
3916
|
+
this.summary?.onTaskUpdate(packs);
|
|
3917
|
+
if (!this.isTTY) {
|
|
3918
|
+
super.onTaskUpdate(packs);
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
onWatcherRerun(files, trigger) {
|
|
3922
|
+
this.summary?.onWatcherRerun();
|
|
3923
|
+
super.onWatcherRerun(files, trigger);
|
|
3924
|
+
}
|
|
3925
|
+
onFinished(files, errors) {
|
|
3926
|
+
this.summary?.onFinished();
|
|
3927
|
+
super.onFinished(files, errors);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
class DotSummary extends TaskParser {
|
|
3931
|
+
renderer;
|
|
3932
|
+
tests = /* @__PURE__ */ new Map();
|
|
3933
|
+
finishedTests = /* @__PURE__ */ new Set();
|
|
3934
|
+
onInit(ctx) {
|
|
3935
|
+
this.ctx = ctx;
|
|
3936
|
+
this.renderer = new WindowRenderer({
|
|
3937
|
+
logger: ctx.logger,
|
|
3938
|
+
getWindow: () => this.createSummary()
|
|
3939
|
+
});
|
|
3940
|
+
this.ctx.onClose(() => this.renderer.stop());
|
|
3941
|
+
}
|
|
3942
|
+
onWatcherRerun() {
|
|
3943
|
+
this.tests.clear();
|
|
3944
|
+
this.renderer.start();
|
|
3945
|
+
}
|
|
3946
|
+
onFinished() {
|
|
3947
|
+
const finalLog = formatTests(Array.from(this.tests.values()));
|
|
3948
|
+
this.ctx.logger.log(finalLog);
|
|
3949
|
+
this.tests.clear();
|
|
3950
|
+
this.renderer.finish();
|
|
3951
|
+
}
|
|
3952
|
+
onTestFilePrepare(file) {
|
|
3953
|
+
for (const test of getTests(file)) {
|
|
3954
|
+
this.onTestStart(test);
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
onTestStart(test) {
|
|
3958
|
+
if (this.finishedTests.has(test.id)) {
|
|
3959
|
+
return;
|
|
3960
|
+
}
|
|
3961
|
+
this.tests.set(test.id, test.mode || "run");
|
|
3962
|
+
}
|
|
3963
|
+
onTestFinished(test) {
|
|
3964
|
+
if (this.finishedTests.has(test.id)) {
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
3967
|
+
this.finishedTests.add(test.id);
|
|
3968
|
+
this.tests.set(test.id, test.result?.state || "skip");
|
|
3969
|
+
}
|
|
3970
|
+
onTestFileFinished() {
|
|
3971
|
+
const columns = this.renderer.getColumns();
|
|
3972
|
+
if (this.tests.size < columns) {
|
|
3973
|
+
return;
|
|
3974
|
+
}
|
|
3975
|
+
const finishedTests = Array.from(this.tests).filter((entry) => entry[1] !== "run");
|
|
3976
|
+
if (finishedTests.length < columns) {
|
|
3977
|
+
return;
|
|
3978
|
+
}
|
|
3979
|
+
const states = [];
|
|
3980
|
+
let count = 0;
|
|
3981
|
+
for (const [id, state] of finishedTests) {
|
|
3982
|
+
if (count++ >= columns) {
|
|
3983
|
+
break;
|
|
3984
|
+
}
|
|
3985
|
+
this.tests.delete(id);
|
|
3986
|
+
states.push(state);
|
|
3987
|
+
}
|
|
3988
|
+
this.ctx.logger.log(formatTests(states));
|
|
3989
|
+
}
|
|
3990
|
+
createSummary() {
|
|
3991
|
+
return [
|
|
3992
|
+
formatTests(Array.from(this.tests.values())),
|
|
3993
|
+
""
|
|
3994
|
+
];
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
const pass = { char: "\xB7", color: c.green };
|
|
3998
|
+
const fail = { char: "x", color: c.red };
|
|
3860
3999
|
const pending = { char: "*", color: c.yellow };
|
|
3861
4000
|
const skip = { char: "-", color: (char) => c.dim(c.gray(char)) };
|
|
3862
|
-
function getIcon(
|
|
3863
|
-
|
|
3864
|
-
return skip;
|
|
3865
|
-
}
|
|
3866
|
-
switch (task.result?.state) {
|
|
4001
|
+
function getIcon(state) {
|
|
4002
|
+
switch (state) {
|
|
3867
4003
|
case "pass":
|
|
3868
|
-
return
|
|
4004
|
+
return pass;
|
|
3869
4005
|
case "fail":
|
|
3870
|
-
return
|
|
4006
|
+
return fail;
|
|
4007
|
+
case "skip":
|
|
4008
|
+
case "todo":
|
|
4009
|
+
return skip;
|
|
3871
4010
|
default:
|
|
3872
4011
|
return pending;
|
|
3873
4012
|
}
|
|
3874
4013
|
}
|
|
3875
|
-
function
|
|
3876
|
-
const all = getTests(tasks);
|
|
4014
|
+
function formatTests(states) {
|
|
3877
4015
|
let currentIcon = pending;
|
|
3878
|
-
let
|
|
3879
|
-
let previousLineWidth = 0;
|
|
4016
|
+
let count = 0;
|
|
3880
4017
|
let output = "";
|
|
3881
|
-
const
|
|
3882
|
-
const
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
output += color(char.repeat(currentTasks));
|
|
3886
|
-
previousLineWidth += currentTasks;
|
|
3887
|
-
} else {
|
|
3888
|
-
let buf = `${char.repeat(availableWidth)}
|
|
3889
|
-
`;
|
|
3890
|
-
const remaining = currentTasks - availableWidth;
|
|
3891
|
-
const fullRows = Math.floor(remaining / width);
|
|
3892
|
-
buf += `${char.repeat(width)}
|
|
3893
|
-
`.repeat(fullRows);
|
|
3894
|
-
const partialRow = remaining % width;
|
|
3895
|
-
if (partialRow > 0) {
|
|
3896
|
-
buf += char.repeat(partialRow);
|
|
3897
|
-
previousLineWidth = partialRow;
|
|
3898
|
-
} else {
|
|
3899
|
-
previousLineWidth = 0;
|
|
3900
|
-
}
|
|
3901
|
-
output += color(buf);
|
|
3902
|
-
}
|
|
3903
|
-
};
|
|
3904
|
-
for (const task of all) {
|
|
3905
|
-
const icon = getIcon(task);
|
|
3906
|
-
if (icon === currentIcon) {
|
|
3907
|
-
currentTasks++;
|
|
4018
|
+
for (const state of states) {
|
|
4019
|
+
const icon = getIcon(state);
|
|
4020
|
+
if (currentIcon === icon) {
|
|
4021
|
+
count++;
|
|
3908
4022
|
continue;
|
|
3909
4023
|
}
|
|
3910
|
-
|
|
3911
|
-
|
|
4024
|
+
output += currentIcon.color(currentIcon.char.repeat(count));
|
|
4025
|
+
count = 1;
|
|
3912
4026
|
currentIcon = icon;
|
|
3913
4027
|
}
|
|
3914
|
-
|
|
4028
|
+
output += currentIcon.color(currentIcon.char.repeat(count));
|
|
3915
4029
|
return output;
|
|
3916
4030
|
}
|
|
3917
|
-
function createDotRenderer(_tasks, options) {
|
|
3918
|
-
let tasks = _tasks;
|
|
3919
|
-
let timer;
|
|
3920
|
-
const { logUpdate: log, outputStream } = options.logger;
|
|
3921
|
-
const columns = "columns" in outputStream ? outputStream.columns : 80;
|
|
3922
|
-
function update() {
|
|
3923
|
-
log(render(tasks, columns));
|
|
3924
|
-
}
|
|
3925
|
-
return {
|
|
3926
|
-
start() {
|
|
3927
|
-
if (timer) {
|
|
3928
|
-
return this;
|
|
3929
|
-
}
|
|
3930
|
-
timer = setInterval(update, 16);
|
|
3931
|
-
return this;
|
|
3932
|
-
},
|
|
3933
|
-
update(_tasks2) {
|
|
3934
|
-
tasks = _tasks2;
|
|
3935
|
-
return this;
|
|
3936
|
-
},
|
|
3937
|
-
async stop() {
|
|
3938
|
-
if (timer) {
|
|
3939
|
-
clearInterval(timer);
|
|
3940
|
-
timer = void 0;
|
|
3941
|
-
}
|
|
3942
|
-
log.clear();
|
|
3943
|
-
options.logger.log(render(tasks, columns));
|
|
3944
|
-
return this;
|
|
3945
|
-
},
|
|
3946
|
-
clear() {
|
|
3947
|
-
log.clear();
|
|
3948
|
-
}
|
|
3949
|
-
};
|
|
3950
|
-
}
|
|
3951
|
-
|
|
3952
|
-
class DotReporter extends BaseReporter {
|
|
3953
|
-
renderer;
|
|
3954
|
-
onTaskUpdate() {
|
|
3955
|
-
}
|
|
3956
|
-
onCollected() {
|
|
3957
|
-
if (this.isTTY) {
|
|
3958
|
-
const files = this.ctx.state.getFiles(this.watchFilters);
|
|
3959
|
-
if (!this.renderer) {
|
|
3960
|
-
this.renderer = createDotRenderer(files, {
|
|
3961
|
-
logger: this.ctx.logger
|
|
3962
|
-
}).start();
|
|
3963
|
-
} else {
|
|
3964
|
-
this.renderer.update(files);
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
3967
|
-
}
|
|
3968
|
-
async onFinished(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
|
|
3969
|
-
await this.stopListRender();
|
|
3970
|
-
this.ctx.logger.log();
|
|
3971
|
-
super.onFinished(files, errors);
|
|
3972
|
-
}
|
|
3973
|
-
async onWatcherStart() {
|
|
3974
|
-
await this.stopListRender();
|
|
3975
|
-
super.onWatcherStart();
|
|
3976
|
-
}
|
|
3977
|
-
async stopListRender() {
|
|
3978
|
-
this.renderer?.stop();
|
|
3979
|
-
this.renderer = void 0;
|
|
3980
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
3981
|
-
}
|
|
3982
|
-
async onWatcherRerun(files, trigger) {
|
|
3983
|
-
await this.stopListRender();
|
|
3984
|
-
super.onWatcherRerun(files, trigger);
|
|
3985
|
-
}
|
|
3986
|
-
onUserConsoleLog(log) {
|
|
3987
|
-
this.renderer?.clear();
|
|
3988
|
-
super.onUserConsoleLog(log);
|
|
3989
|
-
}
|
|
3990
|
-
}
|
|
3991
4031
|
|
|
3992
4032
|
class GithubActionsReporter {
|
|
3993
4033
|
ctx = void 0;
|
|
@@ -3998,14 +4038,14 @@ class GithubActionsReporter {
|
|
|
3998
4038
|
const projectErrors = new Array();
|
|
3999
4039
|
for (const error of errors) {
|
|
4000
4040
|
projectErrors.push({
|
|
4001
|
-
project: this.ctx.
|
|
4041
|
+
project: this.ctx.getRootProject(),
|
|
4002
4042
|
title: "Unhandled error",
|
|
4003
4043
|
error
|
|
4004
4044
|
});
|
|
4005
4045
|
}
|
|
4006
4046
|
for (const file of files) {
|
|
4007
4047
|
const tasks = getTasks(file);
|
|
4008
|
-
const project = this.ctx.
|
|
4048
|
+
const project = this.ctx.getProjectByName(file.projectName || "");
|
|
4009
4049
|
for (const task of tasks) {
|
|
4010
4050
|
if (task.result?.state !== "fail") {
|
|
4011
4051
|
continue;
|
|
@@ -4380,7 +4420,7 @@ class JUnitReporter {
|
|
|
4380
4420
|
const result = capturePrintError(
|
|
4381
4421
|
error,
|
|
4382
4422
|
this.ctx,
|
|
4383
|
-
{ project: this.ctx.
|
|
4423
|
+
{ project: this.ctx.getProjectByName(task.file.projectName || ""), task }
|
|
4384
4424
|
);
|
|
4385
4425
|
await this.baseLog(
|
|
4386
4426
|
escapeXML(stripVTControlCharacters(result.output.trim()))
|
|
@@ -4390,98 +4430,473 @@ class JUnitReporter {
|
|
|
4390
4430
|
}
|
|
4391
4431
|
}
|
|
4392
4432
|
}
|
|
4393
|
-
);
|
|
4433
|
+
);
|
|
4434
|
+
}
|
|
4435
|
+
}
|
|
4436
|
+
async onFinished(files = this.ctx.state.getFiles()) {
|
|
4437
|
+
await this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
|
|
4438
|
+
const transformed = files.map((file) => {
|
|
4439
|
+
const tasks = file.tasks.flatMap((task) => flattenTasks$1(task));
|
|
4440
|
+
const stats2 = tasks.reduce(
|
|
4441
|
+
(stats3, task) => {
|
|
4442
|
+
return {
|
|
4443
|
+
passed: stats3.passed + Number(task.result?.state === "pass"),
|
|
4444
|
+
failures: stats3.failures + Number(task.result?.state === "fail"),
|
|
4445
|
+
skipped: stats3.skipped + Number(task.mode === "skip" || task.mode === "todo")
|
|
4446
|
+
};
|
|
4447
|
+
},
|
|
4448
|
+
{
|
|
4449
|
+
passed: 0,
|
|
4450
|
+
failures: 0,
|
|
4451
|
+
skipped: 0
|
|
4452
|
+
}
|
|
4453
|
+
);
|
|
4454
|
+
const suites = getSuites(file);
|
|
4455
|
+
for (const suite of suites) {
|
|
4456
|
+
if (suite.result?.errors) {
|
|
4457
|
+
tasks.push(suite);
|
|
4458
|
+
stats2.failures += 1;
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4461
|
+
if (tasks.length === 0 && file.result?.state === "fail") {
|
|
4462
|
+
stats2.failures = 1;
|
|
4463
|
+
tasks.push({
|
|
4464
|
+
id: file.id,
|
|
4465
|
+
type: "test",
|
|
4466
|
+
name: file.name,
|
|
4467
|
+
mode: "run",
|
|
4468
|
+
result: file.result,
|
|
4469
|
+
meta: {},
|
|
4470
|
+
// NOTE: not used in JUnitReporter
|
|
4471
|
+
context: null,
|
|
4472
|
+
suite: null,
|
|
4473
|
+
file: null
|
|
4474
|
+
});
|
|
4475
|
+
}
|
|
4476
|
+
return {
|
|
4477
|
+
...file,
|
|
4478
|
+
tasks,
|
|
4479
|
+
stats: stats2
|
|
4480
|
+
};
|
|
4481
|
+
});
|
|
4482
|
+
const stats = transformed.reduce(
|
|
4483
|
+
(stats2, file) => {
|
|
4484
|
+
stats2.tests += file.tasks.length;
|
|
4485
|
+
stats2.failures += file.stats.failures;
|
|
4486
|
+
stats2.time += file.result?.duration || 0;
|
|
4487
|
+
return stats2;
|
|
4488
|
+
},
|
|
4489
|
+
{
|
|
4490
|
+
name: this.options.suiteName || "vitest tests",
|
|
4491
|
+
tests: 0,
|
|
4492
|
+
failures: 0,
|
|
4493
|
+
errors: 0,
|
|
4494
|
+
// we cannot detect those
|
|
4495
|
+
time: 0
|
|
4496
|
+
}
|
|
4497
|
+
);
|
|
4498
|
+
await this.writeElement("testsuites", { ...stats, time: executionTime(stats.time) }, async () => {
|
|
4499
|
+
for (const file of transformed) {
|
|
4500
|
+
const filename = relative(this.ctx.config.root, file.filepath);
|
|
4501
|
+
await this.writeElement(
|
|
4502
|
+
"testsuite",
|
|
4503
|
+
{
|
|
4504
|
+
name: filename,
|
|
4505
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4506
|
+
hostname: hostname(),
|
|
4507
|
+
tests: file.tasks.length,
|
|
4508
|
+
failures: file.stats.failures,
|
|
4509
|
+
errors: 0,
|
|
4510
|
+
// An errored test is one that had an unanticipated problem. We cannot detect those.
|
|
4511
|
+
skipped: file.stats.skipped,
|
|
4512
|
+
time: getDuration(file)
|
|
4513
|
+
},
|
|
4514
|
+
async () => {
|
|
4515
|
+
await this.writeTasks(file.tasks, filename);
|
|
4516
|
+
}
|
|
4517
|
+
);
|
|
4518
|
+
}
|
|
4519
|
+
});
|
|
4520
|
+
if (this.reportFile) {
|
|
4521
|
+
this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
|
|
4522
|
+
}
|
|
4523
|
+
await this.fileFd?.close();
|
|
4524
|
+
this.fileFd = void 0;
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
|
|
4528
|
+
class ReportedTaskImplementation {
|
|
4529
|
+
/**
|
|
4530
|
+
* Task instance.
|
|
4531
|
+
* @internal
|
|
4532
|
+
*/
|
|
4533
|
+
task;
|
|
4534
|
+
/**
|
|
4535
|
+
* The project assosiacted with the test or suite.
|
|
4536
|
+
*/
|
|
4537
|
+
project;
|
|
4538
|
+
/**
|
|
4539
|
+
* Unique identifier.
|
|
4540
|
+
* This ID is deterministic and will be the same for the same test across multiple runs.
|
|
4541
|
+
* The ID is based on the project name, module url and test order.
|
|
4542
|
+
*/
|
|
4543
|
+
id;
|
|
4544
|
+
/**
|
|
4545
|
+
* Location in the module where the test or suite is defined.
|
|
4546
|
+
*/
|
|
4547
|
+
location;
|
|
4548
|
+
/** @internal */
|
|
4549
|
+
constructor(task, project) {
|
|
4550
|
+
this.task = task;
|
|
4551
|
+
this.project = project;
|
|
4552
|
+
this.id = task.id;
|
|
4553
|
+
this.location = task.location;
|
|
4554
|
+
}
|
|
4555
|
+
/**
|
|
4556
|
+
* Checks if the test did not fail the suite.
|
|
4557
|
+
* If the test is not finished yet or was skipped, it will return `true`.
|
|
4558
|
+
*/
|
|
4559
|
+
ok() {
|
|
4560
|
+
const result = this.task.result;
|
|
4561
|
+
return !result || result.state !== "fail";
|
|
4562
|
+
}
|
|
4563
|
+
/**
|
|
4564
|
+
* Creates a new reported task instance and stores it in the project's state for future use.
|
|
4565
|
+
* @internal
|
|
4566
|
+
*/
|
|
4567
|
+
static register(task, project) {
|
|
4568
|
+
const state = new this(task, project);
|
|
4569
|
+
storeTask(project, task, state);
|
|
4570
|
+
return state;
|
|
4571
|
+
}
|
|
4572
|
+
}
|
|
4573
|
+
class TestCase extends ReportedTaskImplementation {
|
|
4574
|
+
#fullName;
|
|
4575
|
+
type = "test";
|
|
4576
|
+
/**
|
|
4577
|
+
* Direct reference to the test module where the test or suite is defined.
|
|
4578
|
+
*/
|
|
4579
|
+
module;
|
|
4580
|
+
/**
|
|
4581
|
+
* Name of the test.
|
|
4582
|
+
*/
|
|
4583
|
+
name;
|
|
4584
|
+
/**
|
|
4585
|
+
* Options that the test was initiated with.
|
|
4586
|
+
*/
|
|
4587
|
+
options;
|
|
4588
|
+
/**
|
|
4589
|
+
* Parent suite. If the test was called directly inside the module, the parent will be the module itself.
|
|
4590
|
+
*/
|
|
4591
|
+
parent;
|
|
4592
|
+
/** @internal */
|
|
4593
|
+
constructor(task, project) {
|
|
4594
|
+
super(task, project);
|
|
4595
|
+
this.name = task.name;
|
|
4596
|
+
this.module = getReportedTask(project, task.file);
|
|
4597
|
+
const suite = this.task.suite;
|
|
4598
|
+
if (suite) {
|
|
4599
|
+
this.parent = getReportedTask(project, suite);
|
|
4600
|
+
} else {
|
|
4601
|
+
this.parent = this.module;
|
|
4602
|
+
}
|
|
4603
|
+
this.options = buildOptions(task);
|
|
4604
|
+
}
|
|
4605
|
+
/**
|
|
4606
|
+
* Full name of the test including all parent suites separated with `>`.
|
|
4607
|
+
*/
|
|
4608
|
+
get fullName() {
|
|
4609
|
+
if (this.#fullName === void 0) {
|
|
4610
|
+
if (this.parent.type !== "module") {
|
|
4611
|
+
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
4612
|
+
} else {
|
|
4613
|
+
this.#fullName = this.name;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
return this.#fullName;
|
|
4617
|
+
}
|
|
4618
|
+
/**
|
|
4619
|
+
* Test results. Will be `undefined` if test is skipped, not finished yet or was just collected.
|
|
4620
|
+
*/
|
|
4621
|
+
result() {
|
|
4622
|
+
const result = this.task.result;
|
|
4623
|
+
if (!result || result.state === "run") {
|
|
4624
|
+
return void 0;
|
|
4625
|
+
}
|
|
4626
|
+
const state = result.state === "fail" ? "failed" : result.state === "pass" ? "passed" : "skipped";
|
|
4627
|
+
if (state === "skipped") {
|
|
4628
|
+
return {
|
|
4629
|
+
state,
|
|
4630
|
+
note: result.note,
|
|
4631
|
+
errors: void 0
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
4634
|
+
if (state === "passed") {
|
|
4635
|
+
return {
|
|
4636
|
+
state,
|
|
4637
|
+
errors: result.errors
|
|
4638
|
+
};
|
|
4639
|
+
}
|
|
4640
|
+
return {
|
|
4641
|
+
state,
|
|
4642
|
+
errors: result.errors || []
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4645
|
+
/**
|
|
4646
|
+
* Checks if the test was skipped during collection or dynamically with `ctx.skip()`.
|
|
4647
|
+
*/
|
|
4648
|
+
skipped() {
|
|
4649
|
+
const mode = this.task.result?.state || this.task.mode;
|
|
4650
|
+
return mode === "skip" || mode === "todo";
|
|
4651
|
+
}
|
|
4652
|
+
/**
|
|
4653
|
+
* Custom metadata that was attached to the test during its execution.
|
|
4654
|
+
*/
|
|
4655
|
+
meta() {
|
|
4656
|
+
return this.task.meta;
|
|
4657
|
+
}
|
|
4658
|
+
/**
|
|
4659
|
+
* Useful information about the test like duration, memory usage, etc.
|
|
4660
|
+
* Diagnostic is only available after the test has finished.
|
|
4661
|
+
*/
|
|
4662
|
+
diagnostic() {
|
|
4663
|
+
const result = this.task.result;
|
|
4664
|
+
if (!result || result.state === "run" || !result.startTime) {
|
|
4665
|
+
return void 0;
|
|
4666
|
+
}
|
|
4667
|
+
const duration = result.duration || 0;
|
|
4668
|
+
const slow = duration > this.project.globalConfig.slowTestThreshold;
|
|
4669
|
+
return {
|
|
4670
|
+
slow,
|
|
4671
|
+
heap: result.heap,
|
|
4672
|
+
duration,
|
|
4673
|
+
startTime: result.startTime,
|
|
4674
|
+
retryCount: result.retryCount ?? 0,
|
|
4675
|
+
repeatCount: result.repeatCount ?? 0,
|
|
4676
|
+
flaky: !!result.retryCount && result.state === "pass" && result.retryCount > 0
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
class TestCollection {
|
|
4681
|
+
#task;
|
|
4682
|
+
#project;
|
|
4683
|
+
constructor(task, project) {
|
|
4684
|
+
this.#task = task;
|
|
4685
|
+
this.#project = project;
|
|
4686
|
+
}
|
|
4687
|
+
/**
|
|
4688
|
+
* Returns the test or suite at a specific index.
|
|
4689
|
+
*/
|
|
4690
|
+
at(index) {
|
|
4691
|
+
if (index < 0) {
|
|
4692
|
+
index = this.size + index;
|
|
4693
|
+
}
|
|
4694
|
+
return getReportedTask(this.#project, this.#task.tasks[index]);
|
|
4695
|
+
}
|
|
4696
|
+
/**
|
|
4697
|
+
* The number of tests and suites in the collection.
|
|
4698
|
+
*/
|
|
4699
|
+
get size() {
|
|
4700
|
+
return this.#task.tasks.length;
|
|
4701
|
+
}
|
|
4702
|
+
/**
|
|
4703
|
+
* Returns the collection in array form for easier manipulation.
|
|
4704
|
+
*/
|
|
4705
|
+
array() {
|
|
4706
|
+
return Array.from(this);
|
|
4707
|
+
}
|
|
4708
|
+
/**
|
|
4709
|
+
* Filters all tests that are part of this collection and its children.
|
|
4710
|
+
*/
|
|
4711
|
+
*allTests(state) {
|
|
4712
|
+
for (const child of this) {
|
|
4713
|
+
if (child.type === "suite") {
|
|
4714
|
+
yield* child.children.allTests(state);
|
|
4715
|
+
} else if (state) {
|
|
4716
|
+
const testState = getTestState(child);
|
|
4717
|
+
if (state === testState) {
|
|
4718
|
+
yield child;
|
|
4719
|
+
}
|
|
4720
|
+
} else {
|
|
4721
|
+
yield child;
|
|
4722
|
+
}
|
|
4394
4723
|
}
|
|
4395
4724
|
}
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
{
|
|
4409
|
-
passed: 0,
|
|
4410
|
-
failures: 0,
|
|
4411
|
-
skipped: 0
|
|
4412
|
-
}
|
|
4413
|
-
);
|
|
4414
|
-
const suites = getSuites(file);
|
|
4415
|
-
for (const suite of suites) {
|
|
4416
|
-
if (suite.result?.errors) {
|
|
4417
|
-
tasks.push(suite);
|
|
4418
|
-
stats2.failures += 1;
|
|
4725
|
+
/**
|
|
4726
|
+
* Filters only the tests that are part of this collection.
|
|
4727
|
+
*/
|
|
4728
|
+
*tests(state) {
|
|
4729
|
+
for (const child of this) {
|
|
4730
|
+
if (child.type !== "test") {
|
|
4731
|
+
continue;
|
|
4732
|
+
}
|
|
4733
|
+
if (state) {
|
|
4734
|
+
const testState = getTestState(child);
|
|
4735
|
+
if (state === testState) {
|
|
4736
|
+
yield child;
|
|
4419
4737
|
}
|
|
4738
|
+
} else {
|
|
4739
|
+
yield child;
|
|
4420
4740
|
}
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
// NOTE: not used in JUnitReporter
|
|
4431
|
-
context: null,
|
|
4432
|
-
suite: null,
|
|
4433
|
-
file: null
|
|
4434
|
-
});
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
/**
|
|
4744
|
+
* Filters only the suites that are part of this collection.
|
|
4745
|
+
*/
|
|
4746
|
+
*suites() {
|
|
4747
|
+
for (const child of this) {
|
|
4748
|
+
if (child.type === "suite") {
|
|
4749
|
+
yield child;
|
|
4435
4750
|
}
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
const
|
|
4443
|
-
(
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
return stats2;
|
|
4447
|
-
},
|
|
4448
|
-
{
|
|
4449
|
-
name: this.options.suiteName || "vitest tests",
|
|
4450
|
-
tests: 0,
|
|
4451
|
-
failures: 0,
|
|
4452
|
-
errors: 0,
|
|
4453
|
-
// we cannot detect those
|
|
4454
|
-
time: executionTime((/* @__PURE__ */ new Date()).getTime() - this._timeStart.getTime())
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
/**
|
|
4754
|
+
* Filters all suites that are part of this collection and its children.
|
|
4755
|
+
*/
|
|
4756
|
+
*allSuites() {
|
|
4757
|
+
for (const child of this) {
|
|
4758
|
+
if (child.type === "suite") {
|
|
4759
|
+
yield child;
|
|
4760
|
+
yield* child.children.allSuites();
|
|
4455
4761
|
}
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
*[Symbol.iterator]() {
|
|
4765
|
+
for (const task of this.#task.tasks) {
|
|
4766
|
+
yield getReportedTask(this.#project, task);
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
class SuiteImplementation extends ReportedTaskImplementation {
|
|
4771
|
+
/**
|
|
4772
|
+
* Collection of suites and tests that are part of this suite.
|
|
4773
|
+
*/
|
|
4774
|
+
children;
|
|
4775
|
+
/** @internal */
|
|
4776
|
+
constructor(task, project) {
|
|
4777
|
+
super(task, project);
|
|
4778
|
+
this.children = new TestCollection(task, project);
|
|
4779
|
+
}
|
|
4780
|
+
/**
|
|
4781
|
+
* Checks if the suite was skipped during collection.
|
|
4782
|
+
*/
|
|
4783
|
+
skipped() {
|
|
4784
|
+
const mode = this.task.mode;
|
|
4785
|
+
return mode === "skip" || mode === "todo";
|
|
4786
|
+
}
|
|
4787
|
+
/**
|
|
4788
|
+
* Errors that happened outside of the test run during collection, like syntax errors.
|
|
4789
|
+
*/
|
|
4790
|
+
errors() {
|
|
4791
|
+
return this.task.result?.errors || [];
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
class TestSuite extends SuiteImplementation {
|
|
4795
|
+
#fullName;
|
|
4796
|
+
type = "suite";
|
|
4797
|
+
/**
|
|
4798
|
+
* Name of the test or the suite.
|
|
4799
|
+
*/
|
|
4800
|
+
name;
|
|
4801
|
+
/**
|
|
4802
|
+
* Direct reference to the test module where the test or suite is defined.
|
|
4803
|
+
*/
|
|
4804
|
+
module;
|
|
4805
|
+
/**
|
|
4806
|
+
* Parent suite. If suite was called directly inside the module, the parent will be the module itself.
|
|
4807
|
+
*/
|
|
4808
|
+
parent;
|
|
4809
|
+
/**
|
|
4810
|
+
* Options that suite was initiated with.
|
|
4811
|
+
*/
|
|
4812
|
+
options;
|
|
4813
|
+
/** @internal */
|
|
4814
|
+
constructor(task, project) {
|
|
4815
|
+
super(task, project);
|
|
4816
|
+
this.name = task.name;
|
|
4817
|
+
this.module = getReportedTask(project, task.file);
|
|
4818
|
+
const suite = this.task.suite;
|
|
4819
|
+
if (suite) {
|
|
4820
|
+
this.parent = getReportedTask(project, suite);
|
|
4821
|
+
} else {
|
|
4822
|
+
this.parent = this.module;
|
|
4823
|
+
}
|
|
4824
|
+
this.options = buildOptions(task);
|
|
4825
|
+
}
|
|
4826
|
+
/**
|
|
4827
|
+
* Full name of the suite including all parent suites separated with `>`.
|
|
4828
|
+
*/
|
|
4829
|
+
get fullName() {
|
|
4830
|
+
if (this.#fullName === void 0) {
|
|
4831
|
+
if (this.parent.type !== "module") {
|
|
4832
|
+
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
4833
|
+
} else {
|
|
4834
|
+
this.#fullName = this.name;
|
|
4477
4835
|
}
|
|
4478
|
-
});
|
|
4479
|
-
if (this.reportFile) {
|
|
4480
|
-
this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
|
|
4481
4836
|
}
|
|
4482
|
-
|
|
4483
|
-
|
|
4837
|
+
return this.#fullName;
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
class TestModule extends SuiteImplementation {
|
|
4841
|
+
type = "module";
|
|
4842
|
+
/**
|
|
4843
|
+
* This is usually an absolute UNIX file path.
|
|
4844
|
+
* It can be a virtual id if the file is not on the disk.
|
|
4845
|
+
* This value corresponds to Vite's `ModuleGraph` id.
|
|
4846
|
+
*/
|
|
4847
|
+
moduleId;
|
|
4848
|
+
/** @internal */
|
|
4849
|
+
constructor(task, project) {
|
|
4850
|
+
super(task, project);
|
|
4851
|
+
this.moduleId = task.filepath;
|
|
4852
|
+
}
|
|
4853
|
+
/**
|
|
4854
|
+
* Useful information about the module like duration, memory usage, etc.
|
|
4855
|
+
* If the module was not executed yet, all diagnostic values will return `0`.
|
|
4856
|
+
*/
|
|
4857
|
+
diagnostic() {
|
|
4858
|
+
const setupDuration = this.task.setupDuration || 0;
|
|
4859
|
+
const collectDuration = this.task.collectDuration || 0;
|
|
4860
|
+
const prepareDuration = this.task.prepareDuration || 0;
|
|
4861
|
+
const environmentSetupDuration = this.task.environmentLoad || 0;
|
|
4862
|
+
const duration = this.task.result?.duration || 0;
|
|
4863
|
+
return {
|
|
4864
|
+
environmentSetupDuration,
|
|
4865
|
+
prepareDuration,
|
|
4866
|
+
collectDuration,
|
|
4867
|
+
setupDuration,
|
|
4868
|
+
duration
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
function buildOptions(task) {
|
|
4873
|
+
return {
|
|
4874
|
+
each: task.each,
|
|
4875
|
+
concurrent: task.concurrent,
|
|
4876
|
+
shuffle: task.shuffle,
|
|
4877
|
+
retry: task.retry,
|
|
4878
|
+
repeats: task.repeats,
|
|
4879
|
+
mode: task.mode
|
|
4880
|
+
};
|
|
4881
|
+
}
|
|
4882
|
+
function getTestState(test) {
|
|
4883
|
+
if (test.skipped()) {
|
|
4884
|
+
return "skipped";
|
|
4885
|
+
}
|
|
4886
|
+
const result = test.result();
|
|
4887
|
+
return result ? result.state : "running";
|
|
4888
|
+
}
|
|
4889
|
+
function storeTask(project, runnerTask, reportedTask) {
|
|
4890
|
+
project.vitest.state.reportedTasksMap.set(runnerTask, reportedTask);
|
|
4891
|
+
}
|
|
4892
|
+
function getReportedTask(project, runnerTask) {
|
|
4893
|
+
const reportedTask = project.vitest.state.getReportedEntity(runnerTask);
|
|
4894
|
+
if (!reportedTask) {
|
|
4895
|
+
throw new Error(
|
|
4896
|
+
`Task instance was not found for ${runnerTask.type} "${runnerTask.name}"`
|
|
4897
|
+
);
|
|
4484
4898
|
}
|
|
4899
|
+
return reportedTask;
|
|
4485
4900
|
}
|
|
4486
4901
|
|
|
4487
4902
|
function yamlString(str) {
|
|
@@ -4532,7 +4947,7 @@ class TapReporter {
|
|
|
4532
4947
|
this.logger.log("}");
|
|
4533
4948
|
} else {
|
|
4534
4949
|
this.logger.log(`${ok} ${id} - ${tapString(task.name)}${comment}`);
|
|
4535
|
-
const project = this.ctx.
|
|
4950
|
+
const project = this.ctx.getProjectByName(task.file.projectName || "");
|
|
4536
4951
|
if (task.result?.state === "fail" && task.result.errors) {
|
|
4537
4952
|
this.logger.indent();
|
|
4538
4953
|
task.result.errors.forEach((error) => {
|