vitest 3.0.0-beta.1 → 3.0.0-beta.3
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/dist/browser.d.ts +12 -9
- package/dist/browser.js +2 -2
- package/dist/chunks/{RandomSequencer.gisBJ77r.js → RandomSequencer.C6x84bNN.js} +4 -3
- package/dist/chunks/{base.CkcgFVQd.js → base.CQ2VEtuH.js} +1 -1
- package/dist/chunks/{cac.CWCZimpS.js → cac.e7qW4xLT.js} +34 -18
- package/dist/chunks/{cli-api.BKUOv0Nc.js → cli-api.CWDlED-m.js} +1126 -537
- package/dist/chunks/{coverage.BoMDb1ip.js → coverage.BWeNbfBa.js} +4 -4
- package/dist/chunks/{creator.DcAcUhMD.js → creator.Ot9GlSGw.js} +16 -14
- package/dist/chunks/{environment.CT0jpO-1.d.ts → environment.d8YfPkTm.d.ts} +1 -3
- package/dist/chunks/{globals.DJTzb7B3.js → globals.BFncSRNA.js} +2 -2
- package/dist/chunks/{index.CkOJwybT.js → index.BBoOXW-l.js} +7 -2
- package/dist/chunks/{index.BqHViJW9.js → index.CkWmZCXU.js} +1 -1
- package/dist/chunks/{index.DKe7vK-G.js → index.CzkCSFCy.js} +670 -628
- package/dist/chunks/{reporters.BZbwTvrM.d.ts → reporters.DCiyjXOg.d.ts} +634 -532
- package/dist/chunks/{resolveConfig.3rGGWga5.js → resolveConfig.C1d7TK-U.js} +5560 -5541
- package/dist/chunks/{runBaseTests.C6huCAng.js → runBaseTests.qNWRkgHj.js} +11 -10
- package/dist/chunks/{setup-common.B5ClyS48.js → setup-common.Cp_bu5q3.js} +1 -1
- package/dist/chunks/types.BOjykUpq.d.ts +27 -0
- package/dist/chunks/{utils.CMUTX-p8.js → utils.Coei4Wlj.js} +1 -1
- package/dist/chunks/{vi.CZKezqeD.js → vi.S4Fq8wSo.js} +2 -1
- package/dist/chunks/{vite.DIfmneq0.d.ts → vite.CRSMFy31.d.ts} +1 -1
- package/dist/chunks/{worker.umPNbBNk.d.ts → worker.R-PA7DpW.d.ts} +1 -1
- package/dist/chunks/{worker.CmzGeuVD.d.ts → worker.XbtCXEXv.d.ts} +4 -3
- package/dist/cli.js +1 -1
- package/dist/config.cjs +1 -0
- package/dist/config.d.ts +6 -8
- package/dist/config.js +1 -0
- package/dist/coverage.d.ts +4 -6
- package/dist/coverage.js +33 -8
- package/dist/environments.d.ts +2 -2
- package/dist/execute.d.ts +2 -2
- package/dist/index.d.ts +11 -14
- package/dist/index.js +2 -2
- package/dist/node.d.ts +22 -17
- package/dist/node.js +66 -32
- package/dist/reporters.d.ts +4 -6
- package/dist/reporters.js +3 -3
- package/dist/runners.d.ts +2 -2
- package/dist/runners.js +3 -3
- package/dist/suite.d.ts +1 -1
- package/dist/workers/forks.js +1 -1
- package/dist/workers/runVmTests.js +7 -7
- package/dist/workers/threads.js +1 -1
- package/dist/workers.d.ts +3 -3
- package/dist/workers.js +1 -1
- package/package.json +15 -15
|
@@ -3,13 +3,12 @@ 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.Coei4Wlj.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
|
|
12
|
-
import { isCI } from 'std-env';
|
|
11
|
+
import { a as TypeCheckError, R as RandomSequencer, g as getOutputFile, b as isTTY } from './RandomSequencer.C6x84bNN.js';
|
|
13
12
|
import { mkdir, writeFile, readdir, stat, readFile } from 'node:fs/promises';
|
|
14
13
|
import { Writable } from 'node:stream';
|
|
15
14
|
import { Console } from 'node:console';
|
|
@@ -20,351 +19,6 @@ import require$$0$1 from 'events';
|
|
|
20
19
|
import { createRequire } from 'node:module';
|
|
21
20
|
import { hostname } from 'node:os';
|
|
22
21
|
|
|
23
|
-
class ReportedTaskImplementation {
|
|
24
|
-
/**
|
|
25
|
-
* Task instance.
|
|
26
|
-
* @experimental Public runner task API is experimental and does not follow semver.
|
|
27
|
-
*/
|
|
28
|
-
task;
|
|
29
|
-
/**
|
|
30
|
-
* The project assosiacted with the test or suite.
|
|
31
|
-
*/
|
|
32
|
-
project;
|
|
33
|
-
/**
|
|
34
|
-
* Unique identifier.
|
|
35
|
-
* This ID is deterministic and will be the same for the same test across multiple runs.
|
|
36
|
-
* The ID is based on the project name, module url and test position.
|
|
37
|
-
*/
|
|
38
|
-
id;
|
|
39
|
-
/**
|
|
40
|
-
* Location in the module where the test or suite is defined.
|
|
41
|
-
*/
|
|
42
|
-
location;
|
|
43
|
-
constructor(task, project) {
|
|
44
|
-
this.task = task;
|
|
45
|
-
this.project = project;
|
|
46
|
-
this.id = task.id;
|
|
47
|
-
this.location = task.location;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Creates a new reported task instance and stores it in the project's state for future use.
|
|
51
|
-
*/
|
|
52
|
-
static register(task, project) {
|
|
53
|
-
const state = new this(task, project);
|
|
54
|
-
storeTask(project, task, state);
|
|
55
|
-
return state;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
class TestCase extends ReportedTaskImplementation {
|
|
59
|
-
#fullName;
|
|
60
|
-
type = "test";
|
|
61
|
-
/**
|
|
62
|
-
* Direct reference to the test module where the test or suite is defined.
|
|
63
|
-
*/
|
|
64
|
-
module;
|
|
65
|
-
/**
|
|
66
|
-
* Name of the test.
|
|
67
|
-
*/
|
|
68
|
-
name;
|
|
69
|
-
/**
|
|
70
|
-
* Options that the test was initiated with.
|
|
71
|
-
*/
|
|
72
|
-
options;
|
|
73
|
-
/**
|
|
74
|
-
* Parent suite. If the test was called directly inside the module, the parent will be the module itself.
|
|
75
|
-
*/
|
|
76
|
-
parent;
|
|
77
|
-
constructor(task, project) {
|
|
78
|
-
super(task, project);
|
|
79
|
-
this.name = task.name;
|
|
80
|
-
this.module = getReportedTask(project, task.file);
|
|
81
|
-
const suite = this.task.suite;
|
|
82
|
-
if (suite) {
|
|
83
|
-
this.parent = getReportedTask(project, suite);
|
|
84
|
-
} else {
|
|
85
|
-
this.parent = this.module;
|
|
86
|
-
}
|
|
87
|
-
this.options = buildOptions(task);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Full name of the test including all parent suites separated with `>`.
|
|
91
|
-
*/
|
|
92
|
-
get fullName() {
|
|
93
|
-
if (this.#fullName === void 0) {
|
|
94
|
-
if (this.parent.type !== "module") {
|
|
95
|
-
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
96
|
-
} else {
|
|
97
|
-
this.#fullName = this.name;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return this.#fullName;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Test results. Will be `undefined` if test is not finished yet or was just collected.
|
|
104
|
-
*/
|
|
105
|
-
result() {
|
|
106
|
-
const result = this.task.result;
|
|
107
|
-
if (!result || result.state === "run") {
|
|
108
|
-
return void 0;
|
|
109
|
-
}
|
|
110
|
-
const state = result.state === "fail" ? "failed" : result.state === "pass" ? "passed" : "skipped";
|
|
111
|
-
if (state === "skipped") {
|
|
112
|
-
return {
|
|
113
|
-
state,
|
|
114
|
-
note: result.note,
|
|
115
|
-
errors: void 0
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
if (state === "passed") {
|
|
119
|
-
return {
|
|
120
|
-
state,
|
|
121
|
-
errors: result.errors
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
return {
|
|
125
|
-
state,
|
|
126
|
-
errors: result.errors || []
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Checks if the test did not fail the suite.
|
|
131
|
-
* If the test is not finished yet or was skipped, it will return `true`.
|
|
132
|
-
*/
|
|
133
|
-
ok() {
|
|
134
|
-
const result = this.result();
|
|
135
|
-
return !result || result.state !== "failed";
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Custom metadata that was attached to the test during its execution.
|
|
139
|
-
*/
|
|
140
|
-
meta() {
|
|
141
|
-
return this.task.meta;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Useful information about the test like duration, memory usage, etc.
|
|
145
|
-
* Diagnostic is only available after the test has finished.
|
|
146
|
-
*/
|
|
147
|
-
diagnostic() {
|
|
148
|
-
const result = this.task.result;
|
|
149
|
-
if (!result || result.state === "run" || !result.startTime) {
|
|
150
|
-
return void 0;
|
|
151
|
-
}
|
|
152
|
-
const duration = result.duration || 0;
|
|
153
|
-
const slow = duration > this.project.globalConfig.slowTestThreshold;
|
|
154
|
-
return {
|
|
155
|
-
slow,
|
|
156
|
-
heap: result.heap,
|
|
157
|
-
duration,
|
|
158
|
-
startTime: result.startTime,
|
|
159
|
-
retryCount: result.retryCount ?? 0,
|
|
160
|
-
repeatCount: result.repeatCount ?? 0,
|
|
161
|
-
flaky: !!result.retryCount && result.state === "pass" && result.retryCount > 0
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
class TestCollection {
|
|
166
|
-
#task;
|
|
167
|
-
#project;
|
|
168
|
-
constructor(task, project) {
|
|
169
|
-
this.#task = task;
|
|
170
|
-
this.#project = project;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Returns the test or suite at a specific index in the array.
|
|
174
|
-
*/
|
|
175
|
-
at(index) {
|
|
176
|
-
if (index < 0) {
|
|
177
|
-
index = this.size + index;
|
|
178
|
-
}
|
|
179
|
-
return getReportedTask(this.#project, this.#task.tasks[index]);
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* The number of tests and suites in the collection.
|
|
183
|
-
*/
|
|
184
|
-
get size() {
|
|
185
|
-
return this.#task.tasks.length;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Returns the collection in array form for easier manipulation.
|
|
189
|
-
*/
|
|
190
|
-
array() {
|
|
191
|
-
return Array.from(this);
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Filters all tests that are part of this collection and its children.
|
|
195
|
-
*/
|
|
196
|
-
*allTests(state) {
|
|
197
|
-
for (const child of this) {
|
|
198
|
-
if (child.type === "suite") {
|
|
199
|
-
yield* child.children.allTests(state);
|
|
200
|
-
} else if (state) {
|
|
201
|
-
const testState = getTestState(child);
|
|
202
|
-
if (state === testState) {
|
|
203
|
-
yield child;
|
|
204
|
-
}
|
|
205
|
-
} else {
|
|
206
|
-
yield child;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Filters only the tests that are part of this collection.
|
|
212
|
-
*/
|
|
213
|
-
*tests(state) {
|
|
214
|
-
for (const child of this) {
|
|
215
|
-
if (child.type !== "test") {
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
if (state) {
|
|
219
|
-
const testState = getTestState(child);
|
|
220
|
-
if (state === testState) {
|
|
221
|
-
yield child;
|
|
222
|
-
}
|
|
223
|
-
} else {
|
|
224
|
-
yield child;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Filters only the suites that are part of this collection.
|
|
230
|
-
*/
|
|
231
|
-
*suites() {
|
|
232
|
-
for (const child of this) {
|
|
233
|
-
if (child.type === "suite") {
|
|
234
|
-
yield child;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* Filters all suites that are part of this collection and its children.
|
|
240
|
-
*/
|
|
241
|
-
*allSuites() {
|
|
242
|
-
for (const child of this) {
|
|
243
|
-
if (child.type === "suite") {
|
|
244
|
-
yield child;
|
|
245
|
-
yield* child.children.allSuites();
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
*[Symbol.iterator]() {
|
|
250
|
-
for (const task of this.#task.tasks) {
|
|
251
|
-
yield getReportedTask(this.#project, task);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
class SuiteImplementation extends ReportedTaskImplementation {
|
|
256
|
-
/**
|
|
257
|
-
* Collection of suites and tests that are part of this suite.
|
|
258
|
-
*/
|
|
259
|
-
children;
|
|
260
|
-
constructor(task, project) {
|
|
261
|
-
super(task, project);
|
|
262
|
-
this.children = new TestCollection(task, project);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
class TestSuite extends SuiteImplementation {
|
|
266
|
-
#fullName;
|
|
267
|
-
type = "suite";
|
|
268
|
-
/**
|
|
269
|
-
* Name of the test or the suite.
|
|
270
|
-
*/
|
|
271
|
-
name;
|
|
272
|
-
/**
|
|
273
|
-
* Direct reference to the test module where the test or suite is defined.
|
|
274
|
-
*/
|
|
275
|
-
module;
|
|
276
|
-
/**
|
|
277
|
-
* Parent suite. If suite was called directly inside the module, the parent will be the module itself.
|
|
278
|
-
*/
|
|
279
|
-
parent;
|
|
280
|
-
/**
|
|
281
|
-
* Options that suite was initiated with.
|
|
282
|
-
*/
|
|
283
|
-
options;
|
|
284
|
-
constructor(task, project) {
|
|
285
|
-
super(task, project);
|
|
286
|
-
this.name = task.name;
|
|
287
|
-
this.module = getReportedTask(project, task.file);
|
|
288
|
-
const suite = this.task.suite;
|
|
289
|
-
if (suite) {
|
|
290
|
-
this.parent = getReportedTask(project, suite);
|
|
291
|
-
} else {
|
|
292
|
-
this.parent = this.module;
|
|
293
|
-
}
|
|
294
|
-
this.options = buildOptions(task);
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Full name of the suite including all parent suites separated with `>`.
|
|
298
|
-
*/
|
|
299
|
-
get fullName() {
|
|
300
|
-
if (this.#fullName === void 0) {
|
|
301
|
-
if (this.parent.type !== "module") {
|
|
302
|
-
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
303
|
-
} else {
|
|
304
|
-
this.#fullName = this.name;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
return this.#fullName;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
class TestModule extends SuiteImplementation {
|
|
311
|
-
type = "module";
|
|
312
|
-
/**
|
|
313
|
-
* This is usually an absolute UNIX file path.
|
|
314
|
-
* It can be a virtual id if the file is not on the disk.
|
|
315
|
-
* This value corresponds to Vite's `ModuleGraph` id.
|
|
316
|
-
*/
|
|
317
|
-
moduleId;
|
|
318
|
-
constructor(task, project) {
|
|
319
|
-
super(task, project);
|
|
320
|
-
this.moduleId = task.filepath;
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Useful information about the module like duration, memory usage, etc.
|
|
324
|
-
* If the module was not executed yet, all diagnostic values will return `0`.
|
|
325
|
-
*/
|
|
326
|
-
diagnostic() {
|
|
327
|
-
const setupDuration = this.task.setupDuration || 0;
|
|
328
|
-
const collectDuration = this.task.collectDuration || 0;
|
|
329
|
-
const prepareDuration = this.task.prepareDuration || 0;
|
|
330
|
-
const environmentSetupDuration = this.task.environmentLoad || 0;
|
|
331
|
-
const duration = this.task.result?.duration || 0;
|
|
332
|
-
return {
|
|
333
|
-
environmentSetupDuration,
|
|
334
|
-
prepareDuration,
|
|
335
|
-
collectDuration,
|
|
336
|
-
setupDuration,
|
|
337
|
-
duration
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function buildOptions(task) {
|
|
342
|
-
return {
|
|
343
|
-
each: task.each,
|
|
344
|
-
concurrent: task.concurrent,
|
|
345
|
-
shuffle: task.shuffle,
|
|
346
|
-
retry: task.retry,
|
|
347
|
-
repeats: task.repeats,
|
|
348
|
-
mode: task.mode
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function getTestState(test) {
|
|
352
|
-
const result = test.result();
|
|
353
|
-
return result ? result.state : "running";
|
|
354
|
-
}
|
|
355
|
-
function storeTask(project, runnerTask, reportedTask) {
|
|
356
|
-
project.vitest.state.reportedTasksMap.set(runnerTask, reportedTask);
|
|
357
|
-
}
|
|
358
|
-
function getReportedTask(project, runnerTask) {
|
|
359
|
-
const reportedTask = project.vitest.state.getReportedEntity(runnerTask);
|
|
360
|
-
if (!reportedTask) {
|
|
361
|
-
throw new Error(
|
|
362
|
-
`Task instance was not found for ${runnerTask.type} "${runnerTask.name}"`
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
return reportedTask;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
22
|
/// <reference types="../types/index.d.ts" />
|
|
369
23
|
|
|
370
24
|
// (c) 2020-present Andrea Giammarchi
|
|
@@ -3350,7 +3004,7 @@ class BaseReporter {
|
|
|
3350
3004
|
_filesInWatchMode = /* @__PURE__ */ new Map();
|
|
3351
3005
|
_timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
3352
3006
|
constructor(options = {}) {
|
|
3353
|
-
this.isTTY = options.isTTY ??
|
|
3007
|
+
this.isTTY = options.isTTY ?? isTTY;
|
|
3354
3008
|
}
|
|
3355
3009
|
onInit(ctx) {
|
|
3356
3010
|
this.ctx = ctx;
|
|
@@ -3379,7 +3033,7 @@ class BaseReporter {
|
|
|
3379
3033
|
}
|
|
3380
3034
|
}
|
|
3381
3035
|
printTask(task) {
|
|
3382
|
-
if (!("filepath" in task) || !task.result?.state || task.result?.state === "run") {
|
|
3036
|
+
if (!("filepath" in task) || !task.result?.state || task.result?.state === "run" || task.result?.state === "queued") {
|
|
3383
3037
|
return;
|
|
3384
3038
|
}
|
|
3385
3039
|
const tests = getTests(task);
|
|
@@ -3426,7 +3080,7 @@ class BaseReporter {
|
|
|
3426
3080
|
} else if (this.ctx.config.hideSkippedTests && (test.mode === "skip" || test.result?.state === "skip")) ; else if (test.result?.state === "skip" && test.result.note) {
|
|
3427
3081
|
this.log(` ${getStateSymbol(test)} ${getTestName(test)}${c.dim(c.gray(` [${test.result.note}]`))}`);
|
|
3428
3082
|
} else if (this.renderSucceed || anyFailed) {
|
|
3429
|
-
this.log(` ${
|
|
3083
|
+
this.log(` ${getStateSymbol(test)} ${getTestName(test, c.dim(" > "))}${suffix2}`);
|
|
3430
3084
|
}
|
|
3431
3085
|
}
|
|
3432
3086
|
}
|
|
@@ -3474,7 +3128,7 @@ class BaseReporter {
|
|
|
3474
3128
|
this.log(BADGE_PADDING + c.dim(" Project name: ") + c.blue(toArray(this.ctx.configOverride.project).join(", ")));
|
|
3475
3129
|
}
|
|
3476
3130
|
if (this.ctx.filenamePattern) {
|
|
3477
|
-
this.log(BADGE_PADDING + c.dim(" Filename pattern: ") + c.blue(this.ctx.filenamePattern));
|
|
3131
|
+
this.log(BADGE_PADDING + c.dim(" Filename pattern: ") + c.blue(this.ctx.filenamePattern.join(", ")));
|
|
3478
3132
|
}
|
|
3479
3133
|
if (this.ctx.configOverride.testNamePattern) {
|
|
3480
3134
|
this.log(BADGE_PADDING + c.dim(" Test name pattern: ") + c.blue(String(this.ctx.configOverride.testNamePattern)));
|
|
@@ -3505,7 +3159,7 @@ class BaseReporter {
|
|
|
3505
3159
|
if (log.browser) {
|
|
3506
3160
|
write("\n");
|
|
3507
3161
|
}
|
|
3508
|
-
const project =
|
|
3162
|
+
const project = task ? this.ctx.getProjectByName(task.file.projectName || "") : this.ctx.getRootProject();
|
|
3509
3163
|
const stack = log.browser ? project.browser?.parseStacktrace(log.origin) || [] : parseStacktrace(log.origin);
|
|
3510
3164
|
const highlight = task && stack.find((i) => i.file === task.file.filepath);
|
|
3511
3165
|
for (const frame of stack) {
|
|
@@ -3687,7 +3341,7 @@ ${errorBanner(`Failed Tests ${failedTests.length}`)}
|
|
|
3687
3341
|
}
|
|
3688
3342
|
const screenshotPaths = tasks2.map((t) => t.meta?.failScreenshotPath).filter((screenshot) => screenshot != null);
|
|
3689
3343
|
this.ctx.logger.printError(error, {
|
|
3690
|
-
project: this.ctx.
|
|
3344
|
+
project: this.ctx.getProjectByName(tasks2[0].file.projectName || ""),
|
|
3691
3345
|
verbose: this.verbose,
|
|
3692
3346
|
screenshotPaths,
|
|
3693
3347
|
task: tasks2[0]
|
|
@@ -3906,7 +3560,7 @@ class TaskParser {
|
|
|
3906
3560
|
for (const pack of packs) {
|
|
3907
3561
|
const task = this.ctx.state.idMap.get(pack[0]);
|
|
3908
3562
|
if (task?.type === "suite" && "filepath" in task && task.result?.state) {
|
|
3909
|
-
if (task?.result?.state === "run") {
|
|
3563
|
+
if (task?.result?.state === "run" || task?.result?.state === "queued") {
|
|
3910
3564
|
startingTestFiles.push(task);
|
|
3911
3565
|
} else {
|
|
3912
3566
|
for (const test of getTests(task)) {
|
|
@@ -3918,7 +3572,7 @@ class TaskParser {
|
|
|
3918
3572
|
}
|
|
3919
3573
|
}
|
|
3920
3574
|
if (task?.type === "test") {
|
|
3921
|
-
if (task.result?.state === "run") {
|
|
3575
|
+
if (task.result?.state === "run" || task.result?.state === "queued") {
|
|
3922
3576
|
startingTests.push(task);
|
|
3923
3577
|
} else if (task.result?.hooks?.afterEach !== "run") {
|
|
3924
3578
|
finishedTests.push(task);
|
|
@@ -3926,7 +3580,7 @@ class TaskParser {
|
|
|
3926
3580
|
}
|
|
3927
3581
|
if (task?.result?.hooks) {
|
|
3928
3582
|
for (const [hook, state] of Object.entries(task.result.hooks)) {
|
|
3929
|
-
if (state === "run") {
|
|
3583
|
+
if (state === "run" || state === "queued") {
|
|
3930
3584
|
startingHooks.push({ name: hook, file: task.file, id: task.id, type: task.type });
|
|
3931
3585
|
} else {
|
|
3932
3586
|
endingHooks.push({ name: hook, file: task.file, id: task.id, type: task.type });
|
|
@@ -3939,9 +3593,7 @@ class TaskParser {
|
|
|
3939
3593
|
finishedTestFiles.forEach((file) => this.onTestFileFinished(file));
|
|
3940
3594
|
startingTestFiles.forEach((file) => this.onTestFilePrepare(file));
|
|
3941
3595
|
startingTests.forEach((test) => this.onTestStart(test));
|
|
3942
|
-
startingHooks.forEach(
|
|
3943
|
-
(hook) => this.onHookStart(hook)
|
|
3944
|
-
);
|
|
3596
|
+
startingHooks.forEach((hook) => this.onHookStart(hook));
|
|
3945
3597
|
}
|
|
3946
3598
|
}
|
|
3947
3599
|
|
|
@@ -3979,6 +3631,9 @@ class SummaryReporter extends TaskParser {
|
|
|
3979
3631
|
this.renderer.stop();
|
|
3980
3632
|
});
|
|
3981
3633
|
}
|
|
3634
|
+
onTestModuleQueued(module) {
|
|
3635
|
+
this.onTestFilePrepare(module.task);
|
|
3636
|
+
}
|
|
3982
3637
|
onPathsCollected(paths) {
|
|
3983
3638
|
this.suites.total = (paths || []).length;
|
|
3984
3639
|
}
|
|
@@ -3999,7 +3654,16 @@ class SummaryReporter extends TaskParser {
|
|
|
3999
3654
|
clearInterval(this.durationInterval);
|
|
4000
3655
|
}
|
|
4001
3656
|
onTestFilePrepare(file) {
|
|
4002
|
-
if (this.
|
|
3657
|
+
if (this.runningTests.has(file.id)) {
|
|
3658
|
+
const stats = this.runningTests.get(file.id);
|
|
3659
|
+
if (!stats.total) {
|
|
3660
|
+
const total2 = getTests(file).length;
|
|
3661
|
+
this.tests.total += total2;
|
|
3662
|
+
stats.total = total2;
|
|
3663
|
+
}
|
|
3664
|
+
return;
|
|
3665
|
+
}
|
|
3666
|
+
if (this.allFinishedTests.has(file.id)) {
|
|
4003
3667
|
return;
|
|
4004
3668
|
}
|
|
4005
3669
|
const total = getTests(file).length;
|
|
@@ -4112,7 +3776,7 @@ class SummaryReporter extends TaskParser {
|
|
|
4112
3776
|
getTestStats(test) {
|
|
4113
3777
|
const file = test.file;
|
|
4114
3778
|
let stats = this.runningTests.get(file.id);
|
|
4115
|
-
if (!stats) {
|
|
3779
|
+
if (!stats || stats.total === 0) {
|
|
4116
3780
|
this.onTestFilePrepare(test.file);
|
|
4117
3781
|
stats = this.runningTests.get(file.id);
|
|
4118
3782
|
if (!stats) {
|
|
@@ -4135,7 +3799,7 @@ class SummaryReporter extends TaskParser {
|
|
|
4135
3799
|
const summary = [""];
|
|
4136
3800
|
for (const testFile of Array.from(this.runningTests.values()).sort(sortRunningTests)) {
|
|
4137
3801
|
summary.push(
|
|
4138
|
-
c.bold(c.yellow(` ${F_POINTER} `)) + formatProjectName(testFile.projectName) + testFile.filename + c.dim(` ${testFile.completed}/${testFile.total}`)
|
|
3802
|
+
c.bold(c.yellow(` ${F_POINTER} `)) + formatProjectName(testFile.projectName) + testFile.filename + c.dim(!testFile.completed && !testFile.total ? " [queued]" : ` ${testFile.completed}/${testFile.total}`)
|
|
4139
3803
|
);
|
|
4140
3804
|
const slowTasks = [
|
|
4141
3805
|
testFile.hook,
|
|
@@ -4219,6 +3883,9 @@ class DefaultReporter extends BaseReporter {
|
|
|
4219
3883
|
this.summary = new SummaryReporter();
|
|
4220
3884
|
}
|
|
4221
3885
|
}
|
|
3886
|
+
onTestModuleQueued(file) {
|
|
3887
|
+
this.summary?.onTestModuleQueued(file);
|
|
3888
|
+
}
|
|
4222
3889
|
onInit(ctx) {
|
|
4223
3890
|
super.onInit(ctx);
|
|
4224
3891
|
this.summary?.onInit(ctx, { verbose: this.verbose });
|
|
@@ -4383,14 +4050,14 @@ class GithubActionsReporter {
|
|
|
4383
4050
|
const projectErrors = new Array();
|
|
4384
4051
|
for (const error of errors) {
|
|
4385
4052
|
projectErrors.push({
|
|
4386
|
-
project: this.ctx.
|
|
4053
|
+
project: this.ctx.getRootProject(),
|
|
4387
4054
|
title: "Unhandled error",
|
|
4388
4055
|
error
|
|
4389
4056
|
});
|
|
4390
4057
|
}
|
|
4391
4058
|
for (const file of files) {
|
|
4392
4059
|
const tasks = getTasks(file);
|
|
4393
|
-
const project = this.ctx.
|
|
4060
|
+
const project = this.ctx.getProjectByName(file.projectName || "");
|
|
4394
4061
|
for (const task of tasks) {
|
|
4395
4062
|
if (task.result?.state !== "fail") {
|
|
4396
4063
|
continue;
|
|
@@ -4464,7 +4131,8 @@ const StatusMap = {
|
|
|
4464
4131
|
pass: "passed",
|
|
4465
4132
|
run: "pending",
|
|
4466
4133
|
skip: "skipped",
|
|
4467
|
-
todo: "todo"
|
|
4134
|
+
todo: "todo",
|
|
4135
|
+
queued: "pending"
|
|
4468
4136
|
};
|
|
4469
4137
|
class JsonReporter {
|
|
4470
4138
|
start = 0;
|
|
@@ -4484,7 +4152,7 @@ class JsonReporter {
|
|
|
4484
4152
|
const numTotalTests = tests.length;
|
|
4485
4153
|
const numFailedTestSuites = suites.filter((s) => s.result?.state === "fail").length;
|
|
4486
4154
|
const numPendingTestSuites = suites.filter(
|
|
4487
|
-
(s) => s.result?.state === "run" || s.mode === "todo"
|
|
4155
|
+
(s) => s.result?.state === "run" || s.result?.state === "queued" || s.mode === "todo"
|
|
4488
4156
|
).length;
|
|
4489
4157
|
const numPassedTestSuites = numTotalTestSuites - numFailedTestSuites - numPendingTestSuites;
|
|
4490
4158
|
const numFailedTests = tests.filter(
|
|
@@ -4492,7 +4160,7 @@ class JsonReporter {
|
|
|
4492
4160
|
).length;
|
|
4493
4161
|
const numPassedTests = tests.filter((t) => t.result?.state === "pass").length;
|
|
4494
4162
|
const numPendingTests = tests.filter(
|
|
4495
|
-
(t) => t.result?.state === "run" || t.mode === "skip" || t.result?.state === "skip"
|
|
4163
|
+
(t) => t.result?.state === "run" || t.result?.state === "queued" || t.mode === "skip" || t.result?.state === "skip"
|
|
4496
4164
|
).length;
|
|
4497
4165
|
const numTodoTests = tests.filter((t) => t.mode === "todo").length;
|
|
4498
4166
|
const testResults = [];
|
|
@@ -4532,7 +4200,7 @@ class JsonReporter {
|
|
|
4532
4200
|
meta: t.meta
|
|
4533
4201
|
};
|
|
4534
4202
|
});
|
|
4535
|
-
if (tests2.some((t) => t.result?.state === "run")) {
|
|
4203
|
+
if (tests2.some((t) => t.result?.state === "run" || t.result?.state === "queued")) {
|
|
4536
4204
|
this.ctx.logger.warn(
|
|
4537
4205
|
"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"
|
|
4538
4206
|
);
|
|
@@ -4581,293 +4249,667 @@ class JsonReporter {
|
|
|
4581
4249
|
if (!existsSync(outputDirectory)) {
|
|
4582
4250
|
await promises.mkdir(outputDirectory, { recursive: true });
|
|
4583
4251
|
}
|
|
4584
|
-
await promises.writeFile(reportFile, report, "utf-8");
|
|
4585
|
-
this.ctx.logger.log(`JSON report written to ${reportFile}`);
|
|
4252
|
+
await promises.writeFile(reportFile, report, "utf-8");
|
|
4253
|
+
this.ctx.logger.log(`JSON report written to ${reportFile}`);
|
|
4254
|
+
} else {
|
|
4255
|
+
this.ctx.logger.log(report);
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
|
|
4260
|
+
class IndentedLogger {
|
|
4261
|
+
constructor(baseLog) {
|
|
4262
|
+
this.baseLog = baseLog;
|
|
4263
|
+
}
|
|
4264
|
+
currentIndent = "";
|
|
4265
|
+
indent() {
|
|
4266
|
+
this.currentIndent += " ";
|
|
4267
|
+
}
|
|
4268
|
+
unindent() {
|
|
4269
|
+
this.currentIndent = this.currentIndent.substring(
|
|
4270
|
+
0,
|
|
4271
|
+
this.currentIndent.length - 4
|
|
4272
|
+
);
|
|
4273
|
+
}
|
|
4274
|
+
log(text) {
|
|
4275
|
+
return this.baseLog(this.currentIndent + text);
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
function flattenTasks$1(task, baseName = "") {
|
|
4280
|
+
const base = baseName ? `${baseName} > ` : "";
|
|
4281
|
+
if (task.type === "suite") {
|
|
4282
|
+
return task.tasks.flatMap(
|
|
4283
|
+
(child) => flattenTasks$1(child, `${base}${task.name}`)
|
|
4284
|
+
);
|
|
4285
|
+
} else {
|
|
4286
|
+
return [
|
|
4287
|
+
{
|
|
4288
|
+
...task,
|
|
4289
|
+
name: `${base}${task.name}`
|
|
4290
|
+
}
|
|
4291
|
+
];
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
function removeInvalidXMLCharacters(value, removeDiscouragedChars) {
|
|
4295
|
+
let regex = /([\0-\x08\v\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g;
|
|
4296
|
+
value = String(value || "").replace(regex, "");
|
|
4297
|
+
{
|
|
4298
|
+
regex = new RegExp(
|
|
4299
|
+
/* eslint-disable regexp/prefer-character-class, regexp/no-obscure-range, regexp/no-useless-non-capturing-group */
|
|
4300
|
+
"([\\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]))",
|
|
4301
|
+
"g"
|
|
4302
|
+
/* eslint-enable */
|
|
4303
|
+
);
|
|
4304
|
+
value = value.replace(regex, "");
|
|
4305
|
+
}
|
|
4306
|
+
return value;
|
|
4307
|
+
}
|
|
4308
|
+
function escapeXML(value) {
|
|
4309
|
+
return removeInvalidXMLCharacters(
|
|
4310
|
+
String(value).replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">"));
|
|
4311
|
+
}
|
|
4312
|
+
function executionTime(durationMS) {
|
|
4313
|
+
return (durationMS / 1e3).toLocaleString("en-US", {
|
|
4314
|
+
useGrouping: false,
|
|
4315
|
+
maximumFractionDigits: 10
|
|
4316
|
+
});
|
|
4317
|
+
}
|
|
4318
|
+
function getDuration(task) {
|
|
4319
|
+
const duration = task.result?.duration ?? 0;
|
|
4320
|
+
return executionTime(duration);
|
|
4321
|
+
}
|
|
4322
|
+
class JUnitReporter {
|
|
4323
|
+
ctx;
|
|
4324
|
+
reportFile;
|
|
4325
|
+
baseLog;
|
|
4326
|
+
logger;
|
|
4327
|
+
_timeStart = /* @__PURE__ */ new Date();
|
|
4328
|
+
fileFd;
|
|
4329
|
+
options;
|
|
4330
|
+
constructor(options) {
|
|
4331
|
+
this.options = { ...options };
|
|
4332
|
+
this.options.includeConsoleOutput ??= true;
|
|
4333
|
+
}
|
|
4334
|
+
async onInit(ctx) {
|
|
4335
|
+
this.ctx = ctx;
|
|
4336
|
+
const outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "junit");
|
|
4337
|
+
if (outputFile) {
|
|
4338
|
+
this.reportFile = resolve(this.ctx.config.root, outputFile);
|
|
4339
|
+
const outputDirectory = dirname(this.reportFile);
|
|
4340
|
+
if (!existsSync(outputDirectory)) {
|
|
4341
|
+
await promises.mkdir(outputDirectory, { recursive: true });
|
|
4342
|
+
}
|
|
4343
|
+
const fileFd = await promises.open(this.reportFile, "w+");
|
|
4344
|
+
this.fileFd = fileFd;
|
|
4345
|
+
this.baseLog = async (text) => {
|
|
4346
|
+
if (!this.fileFd) {
|
|
4347
|
+
this.fileFd = await promises.open(this.reportFile, "w+");
|
|
4348
|
+
}
|
|
4349
|
+
await promises.writeFile(this.fileFd, `${text}
|
|
4350
|
+
`);
|
|
4351
|
+
};
|
|
4586
4352
|
} else {
|
|
4587
|
-
this.ctx.logger.log(
|
|
4353
|
+
this.baseLog = async (text) => this.ctx.logger.log(text);
|
|
4588
4354
|
}
|
|
4355
|
+
this._timeStart = /* @__PURE__ */ new Date();
|
|
4356
|
+
this.logger = new IndentedLogger(this.baseLog);
|
|
4589
4357
|
}
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
this.currentIndent = this.currentIndent.substring(
|
|
4602
|
-
0,
|
|
4603
|
-
this.currentIndent.length - 4
|
|
4358
|
+
async writeElement(name, attrs, children) {
|
|
4359
|
+
const pairs = [];
|
|
4360
|
+
for (const key in attrs) {
|
|
4361
|
+
const attr = attrs[key];
|
|
4362
|
+
if (attr === void 0) {
|
|
4363
|
+
continue;
|
|
4364
|
+
}
|
|
4365
|
+
pairs.push(`${key}="${escapeXML(attr)}"`);
|
|
4366
|
+
}
|
|
4367
|
+
await this.logger.log(
|
|
4368
|
+
`<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`
|
|
4604
4369
|
);
|
|
4370
|
+
this.logger.indent();
|
|
4371
|
+
await children.call(this);
|
|
4372
|
+
this.logger.unindent();
|
|
4373
|
+
await this.logger.log(`</${name}>`);
|
|
4605
4374
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4375
|
+
async writeLogs(task, type) {
|
|
4376
|
+
if (task.logs == null || task.logs.length === 0) {
|
|
4377
|
+
return;
|
|
4378
|
+
}
|
|
4379
|
+
const logType = type === "err" ? "stderr" : "stdout";
|
|
4380
|
+
const logs = task.logs.filter((log) => log.type === logType);
|
|
4381
|
+
if (logs.length === 0) {
|
|
4382
|
+
return;
|
|
4383
|
+
}
|
|
4384
|
+
await this.writeElement(`system-${type}`, {}, async () => {
|
|
4385
|
+
for (const log of logs) {
|
|
4386
|
+
await this.baseLog(escapeXML(log.content));
|
|
4387
|
+
}
|
|
4388
|
+
});
|
|
4608
4389
|
}
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4390
|
+
async writeTasks(tasks, filename) {
|
|
4391
|
+
for (const task of tasks) {
|
|
4392
|
+
let classname = filename;
|
|
4393
|
+
const templateVars = {
|
|
4394
|
+
filename: task.file.name,
|
|
4395
|
+
filepath: task.file.filepath
|
|
4396
|
+
};
|
|
4397
|
+
if (typeof this.options.classnameTemplate === "function") {
|
|
4398
|
+
classname = this.options.classnameTemplate(templateVars);
|
|
4399
|
+
} else if (typeof this.options.classnameTemplate === "string") {
|
|
4400
|
+
classname = this.options.classnameTemplate.replace(/\{filename\}/g, templateVars.filename).replace(/\{filepath\}/g, templateVars.filepath);
|
|
4401
|
+
} else if (typeof this.options.classname === "string") {
|
|
4402
|
+
classname = this.options.classname;
|
|
4403
|
+
}
|
|
4404
|
+
await this.writeElement(
|
|
4405
|
+
"testcase",
|
|
4406
|
+
{
|
|
4407
|
+
classname,
|
|
4408
|
+
file: this.options.addFileAttribute ? filename : void 0,
|
|
4409
|
+
name: task.name,
|
|
4410
|
+
time: getDuration(task)
|
|
4411
|
+
},
|
|
4412
|
+
async () => {
|
|
4413
|
+
if (this.options.includeConsoleOutput) {
|
|
4414
|
+
await this.writeLogs(task, "out");
|
|
4415
|
+
await this.writeLogs(task, "err");
|
|
4416
|
+
}
|
|
4417
|
+
if (task.mode === "skip" || task.mode === "todo") {
|
|
4418
|
+
await this.logger.log("<skipped/>");
|
|
4419
|
+
}
|
|
4420
|
+
if (task.result?.state === "fail") {
|
|
4421
|
+
const errors = task.result.errors || [];
|
|
4422
|
+
for (const error of errors) {
|
|
4423
|
+
await this.writeElement(
|
|
4424
|
+
"failure",
|
|
4425
|
+
{
|
|
4426
|
+
message: error?.message,
|
|
4427
|
+
type: error?.name ?? error?.nameStr
|
|
4428
|
+
},
|
|
4429
|
+
async () => {
|
|
4430
|
+
if (!error) {
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
const result = capturePrintError(
|
|
4434
|
+
error,
|
|
4435
|
+
this.ctx,
|
|
4436
|
+
{ project: this.ctx.getProjectByName(task.file.projectName || ""), task }
|
|
4437
|
+
);
|
|
4438
|
+
await this.baseLog(
|
|
4439
|
+
escapeXML(stripVTControlCharacters(result.output.trim()))
|
|
4440
|
+
);
|
|
4441
|
+
}
|
|
4442
|
+
);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
);
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
async onFinished(files = this.ctx.state.getFiles()) {
|
|
4450
|
+
await this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
|
|
4451
|
+
const transformed = files.map((file) => {
|
|
4452
|
+
const tasks = file.tasks.flatMap((task) => flattenTasks$1(task));
|
|
4453
|
+
const stats2 = tasks.reduce(
|
|
4454
|
+
(stats3, task) => {
|
|
4455
|
+
return {
|
|
4456
|
+
passed: stats3.passed + Number(task.result?.state === "pass"),
|
|
4457
|
+
failures: stats3.failures + Number(task.result?.state === "fail"),
|
|
4458
|
+
skipped: stats3.skipped + Number(task.mode === "skip" || task.mode === "todo")
|
|
4459
|
+
};
|
|
4460
|
+
},
|
|
4461
|
+
{
|
|
4462
|
+
passed: 0,
|
|
4463
|
+
failures: 0,
|
|
4464
|
+
skipped: 0
|
|
4465
|
+
}
|
|
4466
|
+
);
|
|
4467
|
+
const suites = getSuites(file);
|
|
4468
|
+
for (const suite of suites) {
|
|
4469
|
+
if (suite.result?.errors) {
|
|
4470
|
+
tasks.push(suite);
|
|
4471
|
+
stats2.failures += 1;
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
if (tasks.length === 0 && file.result?.state === "fail") {
|
|
4475
|
+
stats2.failures = 1;
|
|
4476
|
+
tasks.push({
|
|
4477
|
+
id: file.id,
|
|
4478
|
+
type: "test",
|
|
4479
|
+
name: file.name,
|
|
4480
|
+
mode: "run",
|
|
4481
|
+
result: file.result,
|
|
4482
|
+
meta: {},
|
|
4483
|
+
// NOTE: not used in JUnitReporter
|
|
4484
|
+
context: null,
|
|
4485
|
+
suite: null,
|
|
4486
|
+
file: null
|
|
4487
|
+
});
|
|
4488
|
+
}
|
|
4489
|
+
return {
|
|
4490
|
+
...file,
|
|
4491
|
+
tasks,
|
|
4492
|
+
stats: stats2
|
|
4493
|
+
};
|
|
4494
|
+
});
|
|
4495
|
+
const stats = transformed.reduce(
|
|
4496
|
+
(stats2, file) => {
|
|
4497
|
+
stats2.tests += file.tasks.length;
|
|
4498
|
+
stats2.failures += file.stats.failures;
|
|
4499
|
+
stats2.time += file.result?.duration || 0;
|
|
4500
|
+
return stats2;
|
|
4501
|
+
},
|
|
4619
4502
|
{
|
|
4620
|
-
|
|
4621
|
-
|
|
4503
|
+
name: this.options.suiteName || "vitest tests",
|
|
4504
|
+
tests: 0,
|
|
4505
|
+
failures: 0,
|
|
4506
|
+
errors: 0,
|
|
4507
|
+
// we cannot detect those
|
|
4508
|
+
time: 0
|
|
4622
4509
|
}
|
|
4623
|
-
|
|
4510
|
+
);
|
|
4511
|
+
await this.writeElement("testsuites", { ...stats, time: executionTime(stats.time) }, async () => {
|
|
4512
|
+
for (const file of transformed) {
|
|
4513
|
+
const filename = relative(this.ctx.config.root, file.filepath);
|
|
4514
|
+
await this.writeElement(
|
|
4515
|
+
"testsuite",
|
|
4516
|
+
{
|
|
4517
|
+
name: filename,
|
|
4518
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4519
|
+
hostname: hostname(),
|
|
4520
|
+
tests: file.tasks.length,
|
|
4521
|
+
failures: file.stats.failures,
|
|
4522
|
+
errors: 0,
|
|
4523
|
+
// An errored test is one that had an unanticipated problem. We cannot detect those.
|
|
4524
|
+
skipped: file.stats.skipped,
|
|
4525
|
+
time: getDuration(file)
|
|
4526
|
+
},
|
|
4527
|
+
async () => {
|
|
4528
|
+
await this.writeTasks(file.tasks, filename);
|
|
4529
|
+
}
|
|
4530
|
+
);
|
|
4531
|
+
}
|
|
4532
|
+
});
|
|
4533
|
+
if (this.reportFile) {
|
|
4534
|
+
this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
|
|
4535
|
+
}
|
|
4536
|
+
await this.fileFd?.close();
|
|
4537
|
+
this.fileFd = void 0;
|
|
4624
4538
|
}
|
|
4625
4539
|
}
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4540
|
+
|
|
4541
|
+
class ReportedTaskImplementation {
|
|
4542
|
+
/**
|
|
4543
|
+
* Task instance.
|
|
4544
|
+
* @internal
|
|
4545
|
+
*/
|
|
4546
|
+
task;
|
|
4547
|
+
/**
|
|
4548
|
+
* The project assosiacted with the test or suite.
|
|
4549
|
+
*/
|
|
4550
|
+
project;
|
|
4551
|
+
/**
|
|
4552
|
+
* Unique identifier.
|
|
4553
|
+
* This ID is deterministic and will be the same for the same test across multiple runs.
|
|
4554
|
+
* The ID is based on the project name, module url and test order.
|
|
4555
|
+
*/
|
|
4556
|
+
id;
|
|
4557
|
+
/**
|
|
4558
|
+
* Location in the module where the test or suite is defined.
|
|
4559
|
+
*/
|
|
4560
|
+
location;
|
|
4561
|
+
/** @internal */
|
|
4562
|
+
constructor(task, project) {
|
|
4563
|
+
this.task = task;
|
|
4564
|
+
this.project = project;
|
|
4565
|
+
this.id = task.id;
|
|
4566
|
+
this.location = task.location;
|
|
4567
|
+
}
|
|
4568
|
+
/**
|
|
4569
|
+
* Checks if the test did not fail the suite.
|
|
4570
|
+
* If the test is not finished yet or was skipped, it will return `true`.
|
|
4571
|
+
*/
|
|
4572
|
+
ok() {
|
|
4573
|
+
const result = this.task.result;
|
|
4574
|
+
return !result || result.state !== "fail";
|
|
4575
|
+
}
|
|
4576
|
+
/**
|
|
4577
|
+
* Creates a new reported task instance and stores it in the project's state for future use.
|
|
4578
|
+
* @internal
|
|
4579
|
+
*/
|
|
4580
|
+
static register(task, project) {
|
|
4581
|
+
const state = new this(task, project);
|
|
4582
|
+
storeTask(project, task, state);
|
|
4583
|
+
return state;
|
|
4637
4584
|
}
|
|
4638
|
-
return value;
|
|
4639
|
-
}
|
|
4640
|
-
function escapeXML(value) {
|
|
4641
|
-
return removeInvalidXMLCharacters(
|
|
4642
|
-
String(value).replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">"));
|
|
4643
|
-
}
|
|
4644
|
-
function executionTime(durationMS) {
|
|
4645
|
-
return (durationMS / 1e3).toLocaleString("en-US", {
|
|
4646
|
-
useGrouping: false,
|
|
4647
|
-
maximumFractionDigits: 10
|
|
4648
|
-
});
|
|
4649
|
-
}
|
|
4650
|
-
function getDuration(task) {
|
|
4651
|
-
const duration = task.result?.duration ?? 0;
|
|
4652
|
-
return executionTime(duration);
|
|
4653
4585
|
}
|
|
4654
|
-
class
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4586
|
+
class TestCase extends ReportedTaskImplementation {
|
|
4587
|
+
#fullName;
|
|
4588
|
+
type = "test";
|
|
4589
|
+
/**
|
|
4590
|
+
* Direct reference to the test module where the test or suite is defined.
|
|
4591
|
+
*/
|
|
4592
|
+
module;
|
|
4593
|
+
/**
|
|
4594
|
+
* Name of the test.
|
|
4595
|
+
*/
|
|
4596
|
+
name;
|
|
4597
|
+
/**
|
|
4598
|
+
* Options that the test was initiated with.
|
|
4599
|
+
*/
|
|
4661
4600
|
options;
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
}
|
|
4675
|
-
const fileFd = await promises.open(this.reportFile, "w+");
|
|
4676
|
-
this.fileFd = fileFd;
|
|
4677
|
-
this.baseLog = async (text) => {
|
|
4678
|
-
if (!this.fileFd) {
|
|
4679
|
-
this.fileFd = await promises.open(this.reportFile, "w+");
|
|
4680
|
-
}
|
|
4681
|
-
await promises.writeFile(this.fileFd, `${text}
|
|
4682
|
-
`);
|
|
4683
|
-
};
|
|
4601
|
+
/**
|
|
4602
|
+
* Parent suite. If the test was called directly inside the module, the parent will be the module itself.
|
|
4603
|
+
*/
|
|
4604
|
+
parent;
|
|
4605
|
+
/** @internal */
|
|
4606
|
+
constructor(task, project) {
|
|
4607
|
+
super(task, project);
|
|
4608
|
+
this.name = task.name;
|
|
4609
|
+
this.module = getReportedTask(project, task.file);
|
|
4610
|
+
const suite = this.task.suite;
|
|
4611
|
+
if (suite) {
|
|
4612
|
+
this.parent = getReportedTask(project, suite);
|
|
4684
4613
|
} else {
|
|
4685
|
-
this.
|
|
4614
|
+
this.parent = this.module;
|
|
4686
4615
|
}
|
|
4687
|
-
this.
|
|
4688
|
-
this.logger = new IndentedLogger(this.baseLog);
|
|
4616
|
+
this.options = buildOptions(task);
|
|
4689
4617
|
}
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4618
|
+
/**
|
|
4619
|
+
* Full name of the test including all parent suites separated with `>`.
|
|
4620
|
+
*/
|
|
4621
|
+
get fullName() {
|
|
4622
|
+
if (this.#fullName === void 0) {
|
|
4623
|
+
if (this.parent.type !== "module") {
|
|
4624
|
+
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
4625
|
+
} else {
|
|
4626
|
+
this.#fullName = this.name;
|
|
4696
4627
|
}
|
|
4697
|
-
pairs.push(`${key}="${escapeXML(attr)}"`);
|
|
4698
4628
|
}
|
|
4699
|
-
|
|
4700
|
-
`<${name}${pairs.length ? ` ${pairs.join(" ")}` : ""}>`
|
|
4701
|
-
);
|
|
4702
|
-
this.logger.indent();
|
|
4703
|
-
await children.call(this);
|
|
4704
|
-
this.logger.unindent();
|
|
4705
|
-
await this.logger.log(`</${name}>`);
|
|
4629
|
+
return this.#fullName;
|
|
4706
4630
|
}
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4631
|
+
/**
|
|
4632
|
+
* Test results. Will be `undefined` if test is skipped, not finished yet or was just collected.
|
|
4633
|
+
*/
|
|
4634
|
+
result() {
|
|
4635
|
+
const result = this.task.result;
|
|
4636
|
+
if (!result || result.state === "run" || result.state === "queued") {
|
|
4637
|
+
return void 0;
|
|
4710
4638
|
}
|
|
4711
|
-
const
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4639
|
+
const state = result.state === "fail" ? "failed" : result.state === "pass" ? "passed" : "skipped";
|
|
4640
|
+
if (state === "skipped") {
|
|
4641
|
+
return {
|
|
4642
|
+
state,
|
|
4643
|
+
note: result.note,
|
|
4644
|
+
errors: void 0
|
|
4645
|
+
};
|
|
4715
4646
|
}
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
});
|
|
4721
|
-
}
|
|
4722
|
-
async writeTasks(tasks, filename) {
|
|
4723
|
-
for (const task of tasks) {
|
|
4724
|
-
let classname = filename;
|
|
4725
|
-
const templateVars = {
|
|
4726
|
-
filename: task.file.name,
|
|
4727
|
-
filepath: task.file.filepath
|
|
4647
|
+
if (state === "passed") {
|
|
4648
|
+
return {
|
|
4649
|
+
state,
|
|
4650
|
+
errors: result.errors
|
|
4728
4651
|
};
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4652
|
+
}
|
|
4653
|
+
return {
|
|
4654
|
+
state,
|
|
4655
|
+
errors: result.errors || []
|
|
4656
|
+
};
|
|
4657
|
+
}
|
|
4658
|
+
/**
|
|
4659
|
+
* Checks if the test was skipped during collection or dynamically with `ctx.skip()`.
|
|
4660
|
+
*/
|
|
4661
|
+
skipped() {
|
|
4662
|
+
const mode = this.task.result?.state || this.task.mode;
|
|
4663
|
+
return mode === "skip" || mode === "todo";
|
|
4664
|
+
}
|
|
4665
|
+
/**
|
|
4666
|
+
* Custom metadata that was attached to the test during its execution.
|
|
4667
|
+
*/
|
|
4668
|
+
meta() {
|
|
4669
|
+
return this.task.meta;
|
|
4670
|
+
}
|
|
4671
|
+
/**
|
|
4672
|
+
* Useful information about the test like duration, memory usage, etc.
|
|
4673
|
+
* Diagnostic is only available after the test has finished.
|
|
4674
|
+
*/
|
|
4675
|
+
diagnostic() {
|
|
4676
|
+
const result = this.task.result;
|
|
4677
|
+
if (!result || result.state === "run" || result.state === "queued" || !result.startTime) {
|
|
4678
|
+
return void 0;
|
|
4679
|
+
}
|
|
4680
|
+
const duration = result.duration || 0;
|
|
4681
|
+
const slow = duration > this.project.globalConfig.slowTestThreshold;
|
|
4682
|
+
return {
|
|
4683
|
+
slow,
|
|
4684
|
+
heap: result.heap,
|
|
4685
|
+
duration,
|
|
4686
|
+
startTime: result.startTime,
|
|
4687
|
+
retryCount: result.retryCount ?? 0,
|
|
4688
|
+
repeatCount: result.repeatCount ?? 0,
|
|
4689
|
+
flaky: !!result.retryCount && result.state === "pass" && result.retryCount > 0
|
|
4690
|
+
};
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
class TestCollection {
|
|
4694
|
+
#task;
|
|
4695
|
+
#project;
|
|
4696
|
+
constructor(task, project) {
|
|
4697
|
+
this.#task = task;
|
|
4698
|
+
this.#project = project;
|
|
4699
|
+
}
|
|
4700
|
+
/**
|
|
4701
|
+
* Returns the test or suite at a specific index.
|
|
4702
|
+
*/
|
|
4703
|
+
at(index) {
|
|
4704
|
+
if (index < 0) {
|
|
4705
|
+
index = this.size + index;
|
|
4706
|
+
}
|
|
4707
|
+
return getReportedTask(this.#project, this.#task.tasks[index]);
|
|
4708
|
+
}
|
|
4709
|
+
/**
|
|
4710
|
+
* The number of tests and suites in the collection.
|
|
4711
|
+
*/
|
|
4712
|
+
get size() {
|
|
4713
|
+
return this.#task.tasks.length;
|
|
4714
|
+
}
|
|
4715
|
+
/**
|
|
4716
|
+
* Returns the collection in array form for easier manipulation.
|
|
4717
|
+
*/
|
|
4718
|
+
array() {
|
|
4719
|
+
return Array.from(this);
|
|
4720
|
+
}
|
|
4721
|
+
/**
|
|
4722
|
+
* Filters all tests that are part of this collection and its children.
|
|
4723
|
+
*/
|
|
4724
|
+
*allTests(state) {
|
|
4725
|
+
for (const child of this) {
|
|
4726
|
+
if (child.type === "suite") {
|
|
4727
|
+
yield* child.children.allTests(state);
|
|
4728
|
+
} else if (state) {
|
|
4729
|
+
const testState = getTestState(child);
|
|
4730
|
+
if (state === testState) {
|
|
4731
|
+
yield child;
|
|
4777
4732
|
}
|
|
4778
|
-
|
|
4733
|
+
} else {
|
|
4734
|
+
yield child;
|
|
4735
|
+
}
|
|
4779
4736
|
}
|
|
4780
4737
|
}
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
{
|
|
4794
|
-
passed: 0,
|
|
4795
|
-
failures: 0,
|
|
4796
|
-
skipped: 0
|
|
4797
|
-
}
|
|
4798
|
-
);
|
|
4799
|
-
const suites = getSuites(file);
|
|
4800
|
-
for (const suite of suites) {
|
|
4801
|
-
if (suite.result?.errors) {
|
|
4802
|
-
tasks.push(suite);
|
|
4803
|
-
stats2.failures += 1;
|
|
4738
|
+
/**
|
|
4739
|
+
* Filters only the tests that are part of this collection.
|
|
4740
|
+
*/
|
|
4741
|
+
*tests(state) {
|
|
4742
|
+
for (const child of this) {
|
|
4743
|
+
if (child.type !== "test") {
|
|
4744
|
+
continue;
|
|
4745
|
+
}
|
|
4746
|
+
if (state) {
|
|
4747
|
+
const testState = getTestState(child);
|
|
4748
|
+
if (state === testState) {
|
|
4749
|
+
yield child;
|
|
4804
4750
|
}
|
|
4751
|
+
} else {
|
|
4752
|
+
yield child;
|
|
4805
4753
|
}
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
// NOTE: not used in JUnitReporter
|
|
4816
|
-
context: null,
|
|
4817
|
-
suite: null,
|
|
4818
|
-
file: null
|
|
4819
|
-
});
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
/**
|
|
4757
|
+
* Filters only the suites that are part of this collection.
|
|
4758
|
+
*/
|
|
4759
|
+
*suites() {
|
|
4760
|
+
for (const child of this) {
|
|
4761
|
+
if (child.type === "suite") {
|
|
4762
|
+
yield child;
|
|
4820
4763
|
}
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
const
|
|
4828
|
-
(
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
stats2.time += file.result?.duration || 0;
|
|
4832
|
-
return stats2;
|
|
4833
|
-
},
|
|
4834
|
-
{
|
|
4835
|
-
name: this.options.suiteName || "vitest tests",
|
|
4836
|
-
tests: 0,
|
|
4837
|
-
failures: 0,
|
|
4838
|
-
errors: 0,
|
|
4839
|
-
// we cannot detect those
|
|
4840
|
-
time: 0
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
/**
|
|
4767
|
+
* Filters all suites that are part of this collection and its children.
|
|
4768
|
+
*/
|
|
4769
|
+
*allSuites() {
|
|
4770
|
+
for (const child of this) {
|
|
4771
|
+
if (child.type === "suite") {
|
|
4772
|
+
yield child;
|
|
4773
|
+
yield* child.children.allSuites();
|
|
4841
4774
|
}
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
*[Symbol.iterator]() {
|
|
4778
|
+
for (const task of this.#task.tasks) {
|
|
4779
|
+
yield getReportedTask(this.#project, task);
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
class SuiteImplementation extends ReportedTaskImplementation {
|
|
4784
|
+
/**
|
|
4785
|
+
* Collection of suites and tests that are part of this suite.
|
|
4786
|
+
*/
|
|
4787
|
+
children;
|
|
4788
|
+
/** @internal */
|
|
4789
|
+
constructor(task, project) {
|
|
4790
|
+
super(task, project);
|
|
4791
|
+
this.children = new TestCollection(task, project);
|
|
4792
|
+
}
|
|
4793
|
+
/**
|
|
4794
|
+
* Checks if the suite was skipped during collection.
|
|
4795
|
+
*/
|
|
4796
|
+
skipped() {
|
|
4797
|
+
const mode = this.task.mode;
|
|
4798
|
+
return mode === "skip" || mode === "todo";
|
|
4799
|
+
}
|
|
4800
|
+
/**
|
|
4801
|
+
* Errors that happened outside of the test run during collection, like syntax errors.
|
|
4802
|
+
*/
|
|
4803
|
+
errors() {
|
|
4804
|
+
return this.task.result?.errors || [];
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
class TestSuite extends SuiteImplementation {
|
|
4808
|
+
#fullName;
|
|
4809
|
+
type = "suite";
|
|
4810
|
+
/**
|
|
4811
|
+
* Name of the test or the suite.
|
|
4812
|
+
*/
|
|
4813
|
+
name;
|
|
4814
|
+
/**
|
|
4815
|
+
* Direct reference to the test module where the test or suite is defined.
|
|
4816
|
+
*/
|
|
4817
|
+
module;
|
|
4818
|
+
/**
|
|
4819
|
+
* Parent suite. If suite was called directly inside the module, the parent will be the module itself.
|
|
4820
|
+
*/
|
|
4821
|
+
parent;
|
|
4822
|
+
/**
|
|
4823
|
+
* Options that suite was initiated with.
|
|
4824
|
+
*/
|
|
4825
|
+
options;
|
|
4826
|
+
/** @internal */
|
|
4827
|
+
constructor(task, project) {
|
|
4828
|
+
super(task, project);
|
|
4829
|
+
this.name = task.name;
|
|
4830
|
+
this.module = getReportedTask(project, task.file);
|
|
4831
|
+
const suite = this.task.suite;
|
|
4832
|
+
if (suite) {
|
|
4833
|
+
this.parent = getReportedTask(project, suite);
|
|
4834
|
+
} else {
|
|
4835
|
+
this.parent = this.module;
|
|
4836
|
+
}
|
|
4837
|
+
this.options = buildOptions(task);
|
|
4838
|
+
}
|
|
4839
|
+
/**
|
|
4840
|
+
* Full name of the suite including all parent suites separated with `>`.
|
|
4841
|
+
*/
|
|
4842
|
+
get fullName() {
|
|
4843
|
+
if (this.#fullName === void 0) {
|
|
4844
|
+
if (this.parent.type !== "module") {
|
|
4845
|
+
this.#fullName = `${this.parent.fullName} > ${this.name}`;
|
|
4846
|
+
} else {
|
|
4847
|
+
this.#fullName = this.name;
|
|
4863
4848
|
}
|
|
4864
|
-
});
|
|
4865
|
-
if (this.reportFile) {
|
|
4866
|
-
this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
|
|
4867
4849
|
}
|
|
4868
|
-
|
|
4869
|
-
|
|
4850
|
+
return this.#fullName;
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
class TestModule extends SuiteImplementation {
|
|
4854
|
+
type = "module";
|
|
4855
|
+
/**
|
|
4856
|
+
* This is usually an absolute UNIX file path.
|
|
4857
|
+
* It can be a virtual id if the file is not on the disk.
|
|
4858
|
+
* This value corresponds to Vite's `ModuleGraph` id.
|
|
4859
|
+
*/
|
|
4860
|
+
moduleId;
|
|
4861
|
+
/** @internal */
|
|
4862
|
+
constructor(task, project) {
|
|
4863
|
+
super(task, project);
|
|
4864
|
+
this.moduleId = task.filepath;
|
|
4865
|
+
}
|
|
4866
|
+
/**
|
|
4867
|
+
* Useful information about the module like duration, memory usage, etc.
|
|
4868
|
+
* If the module was not executed yet, all diagnostic values will return `0`.
|
|
4869
|
+
*/
|
|
4870
|
+
diagnostic() {
|
|
4871
|
+
const setupDuration = this.task.setupDuration || 0;
|
|
4872
|
+
const collectDuration = this.task.collectDuration || 0;
|
|
4873
|
+
const prepareDuration = this.task.prepareDuration || 0;
|
|
4874
|
+
const environmentSetupDuration = this.task.environmentLoad || 0;
|
|
4875
|
+
const duration = this.task.result?.duration || 0;
|
|
4876
|
+
return {
|
|
4877
|
+
environmentSetupDuration,
|
|
4878
|
+
prepareDuration,
|
|
4879
|
+
collectDuration,
|
|
4880
|
+
setupDuration,
|
|
4881
|
+
duration
|
|
4882
|
+
};
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
function buildOptions(task) {
|
|
4886
|
+
return {
|
|
4887
|
+
each: task.each,
|
|
4888
|
+
concurrent: task.concurrent,
|
|
4889
|
+
shuffle: task.shuffle,
|
|
4890
|
+
retry: task.retry,
|
|
4891
|
+
repeats: task.repeats,
|
|
4892
|
+
mode: task.mode
|
|
4893
|
+
};
|
|
4894
|
+
}
|
|
4895
|
+
function getTestState(test) {
|
|
4896
|
+
if (test.skipped()) {
|
|
4897
|
+
return "skipped";
|
|
4898
|
+
}
|
|
4899
|
+
const result = test.result();
|
|
4900
|
+
return result ? result.state : "running";
|
|
4901
|
+
}
|
|
4902
|
+
function storeTask(project, runnerTask, reportedTask) {
|
|
4903
|
+
project.vitest.state.reportedTasksMap.set(runnerTask, reportedTask);
|
|
4904
|
+
}
|
|
4905
|
+
function getReportedTask(project, runnerTask) {
|
|
4906
|
+
const reportedTask = project.vitest.state.getReportedEntity(runnerTask);
|
|
4907
|
+
if (!reportedTask) {
|
|
4908
|
+
throw new Error(
|
|
4909
|
+
`Task instance was not found for ${runnerTask.type} "${runnerTask.name}"`
|
|
4910
|
+
);
|
|
4870
4911
|
}
|
|
4912
|
+
return reportedTask;
|
|
4871
4913
|
}
|
|
4872
4914
|
|
|
4873
4915
|
function yamlString(str) {
|
|
@@ -4918,7 +4960,7 @@ class TapReporter {
|
|
|
4918
4960
|
this.logger.log("}");
|
|
4919
4961
|
} else {
|
|
4920
4962
|
this.logger.log(`${ok} ${id} - ${tapString(task.name)}${comment}`);
|
|
4921
|
-
const project = this.ctx.
|
|
4963
|
+
const project = this.ctx.getProjectByName(task.file.projectName || "");
|
|
4922
4964
|
if (task.result?.state === "fail" && task.result.errors) {
|
|
4923
4965
|
this.logger.indent();
|
|
4924
4966
|
task.result.errors.forEach((error) => {
|
|
@@ -4990,7 +5032,7 @@ class VerboseReporter extends DefaultReporter {
|
|
|
4990
5032
|
}
|
|
4991
5033
|
for (const pack of packs) {
|
|
4992
5034
|
const task = this.ctx.state.idMap.get(pack[0]);
|
|
4993
|
-
if (task && task.type === "test" && task.result?.state && task.result?.state !== "run") {
|
|
5035
|
+
if (task && task.type === "test" && task.result?.state && task.result?.state !== "run" && task.result?.state !== "queued") {
|
|
4994
5036
|
let title = ` ${getStateSymbol(task)} `;
|
|
4995
5037
|
if (task.file.projectName) {
|
|
4996
5038
|
title += formatProjectName(task.file.projectName);
|
|
@@ -5294,9 +5336,9 @@ class TableReporter extends BaseReporter {
|
|
|
5294
5336
|
}
|
|
5295
5337
|
for (const pack of packs) {
|
|
5296
5338
|
const task = this.ctx.state.idMap.get(pack[0]);
|
|
5297
|
-
if (task && task.type === "suite" && task.result?.state && task.result?.state !== "run") {
|
|
5339
|
+
if (task && task.type === "suite" && task.result?.state && task.result?.state !== "run" && task.result?.state !== "queued") {
|
|
5298
5340
|
const benches = task.tasks.filter((t) => t.meta.benchmark);
|
|
5299
|
-
if (benches.length > 0 && benches.every((t) => t.result?.state !== "run")) {
|
|
5341
|
+
if (benches.length > 0 && benches.every((t) => t.result?.state !== "run" && t.result?.state !== "queued")) {
|
|
5300
5342
|
let title = ` ${getStateSymbol(task)} ${getFullName(
|
|
5301
5343
|
task,
|
|
5302
5344
|
c.dim(" > ")
|