@vitest/runner 2.0.0-beta.9 → 2.0.1
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/chunk-tasks.js +77 -24
- package/dist/index.d.ts +6 -5
- package/dist/index.js +342 -154
- package/dist/{tasks-Cjrz1dUg.d.ts → tasks-WAKtRuk9.d.ts} +28 -4
- package/dist/types.d.ts +11 -3
- package/dist/utils.d.ts +10 -4
- package/dist/utils.js +1 -1
- package/package.json +2 -3
package/dist/index.js
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
import limit from 'p-limit';
|
2
1
|
import { getSafeTimers, isObject, createDefer, isNegativeNaN, format, objDisplay, objectAttr, toArray, shuffle } from '@vitest/utils';
|
3
2
|
import { processError } from '@vitest/utils/error';
|
4
3
|
export { processError } from '@vitest/utils/error';
|
5
|
-
import {
|
4
|
+
import { l as createChainable, a as createFileTask, c as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, m as limitConcurrency, p as partitionSuiteChildren, h as hasTests, j as hasFailed } from './chunk-tasks.js';
|
6
5
|
import { parseSingleStack } from '@vitest/utils/source-map';
|
7
6
|
import 'pathe';
|
8
7
|
|
@@ -53,18 +52,22 @@ async function runWithSuite(suite, fn) {
|
|
53
52
|
collectorContext.currentSuite = prev;
|
54
53
|
}
|
55
54
|
function withTimeout(fn, timeout, isHook = false) {
|
56
|
-
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY)
|
55
|
+
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY) {
|
57
56
|
return fn;
|
57
|
+
}
|
58
58
|
const { setTimeout, clearTimeout } = getSafeTimers();
|
59
59
|
return (...args) => {
|
60
|
-
return Promise.race([
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
60
|
+
return Promise.race([
|
61
|
+
fn(...args),
|
62
|
+
new Promise((resolve, reject) => {
|
63
|
+
var _a;
|
64
|
+
const timer = setTimeout(() => {
|
65
|
+
clearTimeout(timer);
|
66
|
+
reject(new Error(makeTimeoutMsg(isHook, timeout)));
|
67
|
+
}, timeout);
|
68
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
69
|
+
})
|
70
|
+
]);
|
68
71
|
};
|
69
72
|
}
|
70
73
|
function createTestContext(test, runner) {
|
@@ -94,25 +97,31 @@ If this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as t
|
|
94
97
|
|
95
98
|
function mergeContextFixtures(fixtures, context = {}) {
|
96
99
|
const fixtureOptionKeys = ["auto"];
|
97
|
-
const fixtureArray = Object.entries(fixtures).map(
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
const fixtureArray = Object.entries(fixtures).map(
|
101
|
+
([prop, value]) => {
|
102
|
+
const fixtureItem = { value };
|
103
|
+
if (Array.isArray(value) && value.length >= 2 && isObject(value[1]) && Object.keys(value[1]).some((key) => fixtureOptionKeys.includes(key))) {
|
104
|
+
Object.assign(fixtureItem, value[1]);
|
105
|
+
fixtureItem.value = value[0];
|
106
|
+
}
|
107
|
+
fixtureItem.prop = prop;
|
108
|
+
fixtureItem.isFn = typeof fixtureItem.value === "function";
|
109
|
+
return fixtureItem;
|
102
110
|
}
|
103
|
-
|
104
|
-
|
105
|
-
return fixtureItem;
|
106
|
-
});
|
107
|
-
if (Array.isArray(context.fixtures))
|
111
|
+
);
|
112
|
+
if (Array.isArray(context.fixtures)) {
|
108
113
|
context.fixtures = context.fixtures.concat(fixtureArray);
|
109
|
-
else
|
114
|
+
} else {
|
110
115
|
context.fixtures = fixtureArray;
|
116
|
+
}
|
111
117
|
fixtureArray.forEach((fixture) => {
|
112
118
|
if (fixture.isFn) {
|
113
119
|
const usedProps = getUsedProps(fixture.value);
|
114
|
-
if (usedProps.length)
|
115
|
-
fixture.deps = context.fixtures.filter(
|
120
|
+
if (usedProps.length) {
|
121
|
+
fixture.deps = context.fixtures.filter(
|
122
|
+
({ prop }) => prop !== fixture.prop && usedProps.includes(prop)
|
123
|
+
);
|
124
|
+
}
|
116
125
|
}
|
117
126
|
});
|
118
127
|
return context;
|
@@ -121,36 +130,46 @@ const fixtureValueMaps = /* @__PURE__ */ new Map();
|
|
121
130
|
const cleanupFnArrayMap = /* @__PURE__ */ new Map();
|
122
131
|
async function callFixtureCleanup(context) {
|
123
132
|
const cleanupFnArray = cleanupFnArrayMap.get(context) ?? [];
|
124
|
-
for (const cleanup of cleanupFnArray.reverse())
|
133
|
+
for (const cleanup of cleanupFnArray.reverse()) {
|
125
134
|
await cleanup();
|
135
|
+
}
|
126
136
|
cleanupFnArrayMap.delete(context);
|
127
137
|
}
|
128
138
|
function withFixtures(fn, testContext) {
|
129
139
|
return (hookContext) => {
|
130
140
|
const context = hookContext || testContext;
|
131
|
-
if (!context)
|
141
|
+
if (!context) {
|
132
142
|
return fn({});
|
143
|
+
}
|
133
144
|
const fixtures = getFixture(context);
|
134
|
-
if (!(fixtures == null ? void 0 : fixtures.length))
|
145
|
+
if (!(fixtures == null ? void 0 : fixtures.length)) {
|
135
146
|
return fn(context);
|
147
|
+
}
|
136
148
|
const usedProps = getUsedProps(fn);
|
137
149
|
const hasAutoFixture = fixtures.some(({ auto }) => auto);
|
138
|
-
if (!usedProps.length && !hasAutoFixture)
|
150
|
+
if (!usedProps.length && !hasAutoFixture) {
|
139
151
|
return fn(context);
|
140
|
-
|
152
|
+
}
|
153
|
+
if (!fixtureValueMaps.get(context)) {
|
141
154
|
fixtureValueMaps.set(context, /* @__PURE__ */ new Map());
|
155
|
+
}
|
142
156
|
const fixtureValueMap = fixtureValueMaps.get(context);
|
143
|
-
if (!cleanupFnArrayMap.has(context))
|
157
|
+
if (!cleanupFnArrayMap.has(context)) {
|
144
158
|
cleanupFnArrayMap.set(context, []);
|
159
|
+
}
|
145
160
|
const cleanupFnArray = cleanupFnArrayMap.get(context);
|
146
|
-
const usedFixtures = fixtures.filter(
|
161
|
+
const usedFixtures = fixtures.filter(
|
162
|
+
({ prop, auto }) => auto || usedProps.includes(prop)
|
163
|
+
);
|
147
164
|
const pendingFixtures = resolveDeps(usedFixtures);
|
148
|
-
if (!pendingFixtures.length)
|
165
|
+
if (!pendingFixtures.length) {
|
149
166
|
return fn(context);
|
167
|
+
}
|
150
168
|
async function resolveFixtures() {
|
151
169
|
for (const fixture of pendingFixtures) {
|
152
|
-
if (fixtureValueMap.has(fixture))
|
170
|
+
if (fixtureValueMap.has(fixture)) {
|
153
171
|
continue;
|
172
|
+
}
|
154
173
|
const resolvedValue = fixture.isFn ? await resolveFixtureFunction(fixture.value, context, cleanupFnArray) : fixture.value;
|
155
174
|
context[fixture.prop] = resolvedValue;
|
156
175
|
fixtureValueMap.set(fixture, resolvedValue);
|
@@ -185,14 +204,18 @@ async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
|
|
185
204
|
}
|
186
205
|
function resolveDeps(fixtures, depSet = /* @__PURE__ */ new Set(), pendingFixtures = []) {
|
187
206
|
fixtures.forEach((fixture) => {
|
188
|
-
if (pendingFixtures.includes(fixture))
|
207
|
+
if (pendingFixtures.includes(fixture)) {
|
189
208
|
return;
|
209
|
+
}
|
190
210
|
if (!fixture.isFn || !fixture.deps) {
|
191
211
|
pendingFixtures.push(fixture);
|
192
212
|
return;
|
193
213
|
}
|
194
|
-
if (depSet.has(fixture))
|
195
|
-
throw new Error(
|
214
|
+
if (depSet.has(fixture)) {
|
215
|
+
throw new Error(
|
216
|
+
`Circular fixture dependency detected: ${fixture.prop} <- ${[...depSet].reverse().map((d) => d.prop).join(" <- ")}`
|
217
|
+
);
|
218
|
+
}
|
196
219
|
depSet.add(fixture);
|
197
220
|
resolveDeps(fixture.deps, depSet, pendingFixtures);
|
198
221
|
pendingFixtures.push(fixture);
|
@@ -202,21 +225,35 @@ function resolveDeps(fixtures, depSet = /* @__PURE__ */ new Set(), pendingFixtur
|
|
202
225
|
}
|
203
226
|
function getUsedProps(fn) {
|
204
227
|
const match = fn.toString().match(/[^(]*\(([^)]*)/);
|
205
|
-
if (!match)
|
228
|
+
if (!match) {
|
206
229
|
return [];
|
230
|
+
}
|
207
231
|
const args = splitByComma(match[1]);
|
208
|
-
if (!args.length)
|
232
|
+
if (!args.length) {
|
209
233
|
return [];
|
210
|
-
|
211
|
-
|
212
|
-
|
234
|
+
}
|
235
|
+
let first = args[0];
|
236
|
+
if ("__VITEST_FIXTURE_INDEX__" in fn) {
|
237
|
+
first = args[fn.__VITEST_FIXTURE_INDEX__];
|
238
|
+
if (!first) {
|
239
|
+
return [];
|
240
|
+
}
|
241
|
+
}
|
242
|
+
if (!(first.startsWith("{") && first.endsWith("}"))) {
|
243
|
+
throw new Error(
|
244
|
+
`The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "${first}".`
|
245
|
+
);
|
246
|
+
}
|
213
247
|
const _first = first.slice(1, -1).replace(/\s/g, "");
|
214
248
|
const props = splitByComma(_first).map((prop) => {
|
215
249
|
return prop.replace(/:.*|=.*/g, "");
|
216
250
|
});
|
217
251
|
const last = props.at(-1);
|
218
|
-
if (last && last.startsWith("..."))
|
219
|
-
throw new Error(
|
252
|
+
if (last && last.startsWith("...")) {
|
253
|
+
throw new Error(
|
254
|
+
`Rest parameters are not supported in fixtures, received "${last}".`
|
255
|
+
);
|
256
|
+
}
|
220
257
|
return props;
|
221
258
|
}
|
222
259
|
function splitByComma(s) {
|
@@ -230,14 +267,16 @@ function splitByComma(s) {
|
|
230
267
|
stack.pop();
|
231
268
|
} else if (!stack.length && s[i] === ",") {
|
232
269
|
const token = s.substring(start, i).trim();
|
233
|
-
if (token)
|
270
|
+
if (token) {
|
234
271
|
result.push(token);
|
272
|
+
}
|
235
273
|
start = i + 1;
|
236
274
|
}
|
237
275
|
}
|
238
276
|
const lastToken = s.substring(start).trim();
|
239
|
-
if (lastToken)
|
277
|
+
if (lastToken) {
|
240
278
|
result.push(lastToken);
|
279
|
+
}
|
241
280
|
return result;
|
242
281
|
}
|
243
282
|
|
@@ -250,13 +289,19 @@ function getCurrentTest() {
|
|
250
289
|
}
|
251
290
|
|
252
291
|
const suite = createSuite();
|
253
|
-
const test = createTest(
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
292
|
+
const test = createTest(function(name, optionsOrFn, optionsOrTest) {
|
293
|
+
if (getCurrentTest()) {
|
294
|
+
throw new Error(
|
295
|
+
'Calling the test function inside another test function is not allowed. Please put it inside "describe" or "suite" so it can be properly collected.'
|
296
|
+
);
|
258
297
|
}
|
259
|
-
)
|
298
|
+
getCurrentSuite().test.fn.call(
|
299
|
+
this,
|
300
|
+
formatName(name),
|
301
|
+
optionsOrFn,
|
302
|
+
optionsOrTest
|
303
|
+
);
|
304
|
+
});
|
260
305
|
const describe = suite;
|
261
306
|
const it = test;
|
262
307
|
let runner;
|
@@ -278,8 +323,9 @@ function createDefaultSuite(runner2) {
|
|
278
323
|
});
|
279
324
|
}
|
280
325
|
function clearCollectorContext(filepath, currentRunner) {
|
281
|
-
if (!defaultSuite)
|
326
|
+
if (!defaultSuite) {
|
282
327
|
defaultSuite = createDefaultSuite(currentRunner);
|
328
|
+
}
|
283
329
|
runner = currentRunner;
|
284
330
|
currentTestFilepath = filepath;
|
285
331
|
collectorContext.tasks.length = 0;
|
@@ -302,8 +348,11 @@ function parseArguments(optionsOrFn, optionsOrTest) {
|
|
302
348
|
let fn = () => {
|
303
349
|
};
|
304
350
|
if (typeof optionsOrTest === "object") {
|
305
|
-
if (typeof optionsOrFn === "object")
|
306
|
-
throw new TypeError(
|
351
|
+
if (typeof optionsOrFn === "object") {
|
352
|
+
throw new TypeError(
|
353
|
+
"Cannot use two objects as arguments. Please provide options and a function callback in that order."
|
354
|
+
);
|
355
|
+
}
|
307
356
|
options = optionsOrTest;
|
308
357
|
} else if (typeof optionsOrTest === "number") {
|
309
358
|
options = { timeout: optionsOrTest };
|
@@ -311,8 +360,11 @@ function parseArguments(optionsOrFn, optionsOrTest) {
|
|
311
360
|
options = optionsOrFn;
|
312
361
|
}
|
313
362
|
if (typeof optionsOrFn === "function") {
|
314
|
-
if (typeof optionsOrTest === "function")
|
315
|
-
throw new TypeError(
|
363
|
+
if (typeof optionsOrTest === "function") {
|
364
|
+
throw new TypeError(
|
365
|
+
"Cannot use two functions as arguments. Please use the second argument for options."
|
366
|
+
);
|
367
|
+
}
|
316
368
|
fn = optionsOrFn;
|
317
369
|
} else if (typeof optionsOrTest === "function") {
|
318
370
|
fn = optionsOrTest;
|
@@ -344,10 +396,12 @@ function createSuiteCollector(name, factory = () => {
|
|
344
396
|
meta: options.meta ?? /* @__PURE__ */ Object.create(null)
|
345
397
|
};
|
346
398
|
const handler = options.handler;
|
347
|
-
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent)
|
399
|
+
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent) {
|
348
400
|
task2.concurrent = true;
|
349
|
-
|
401
|
+
}
|
402
|
+
if (shuffle) {
|
350
403
|
task2.shuffle = true;
|
404
|
+
}
|
351
405
|
const context = createTestContext(task2, runner);
|
352
406
|
Object.defineProperty(task2, "context", {
|
353
407
|
value: context,
|
@@ -355,10 +409,13 @@ function createSuiteCollector(name, factory = () => {
|
|
355
409
|
});
|
356
410
|
setFixture(context, options.fixtures);
|
357
411
|
if (handler) {
|
358
|
-
setFn(
|
359
|
-
|
360
|
-
(
|
361
|
-
|
412
|
+
setFn(
|
413
|
+
task2,
|
414
|
+
withTimeout(
|
415
|
+
withFixtures(handler, context),
|
416
|
+
(options == null ? void 0 : options.timeout) ?? runner.config.testTimeout
|
417
|
+
)
|
418
|
+
);
|
362
419
|
}
|
363
420
|
if (runner.config.includeTaskLocation) {
|
364
421
|
const limit = Error.stackTraceLimit;
|
@@ -366,25 +423,25 @@ function createSuiteCollector(name, factory = () => {
|
|
366
423
|
const error = new Error("stacktrace").stack;
|
367
424
|
Error.stackTraceLimit = limit;
|
368
425
|
const stack = findTestFileStackTrace(error, task2.each ?? false);
|
369
|
-
if (stack)
|
426
|
+
if (stack) {
|
370
427
|
task2.location = stack;
|
428
|
+
}
|
371
429
|
}
|
372
430
|
tasks.push(task2);
|
373
431
|
return task2;
|
374
432
|
};
|
375
433
|
const test2 = createTest(function(name2, optionsOrFn, optionsOrTest) {
|
376
|
-
let { options, handler } = parseArguments(
|
377
|
-
|
378
|
-
optionsOrTest
|
379
|
-
);
|
380
|
-
if (typeof suiteOptions === "object")
|
434
|
+
let { options, handler } = parseArguments(optionsOrFn, optionsOrTest);
|
435
|
+
if (typeof suiteOptions === "object") {
|
381
436
|
options = Object.assign({}, suiteOptions, options);
|
437
|
+
}
|
382
438
|
options.concurrent = this.concurrent || !this.sequential && (options == null ? void 0 : options.concurrent);
|
383
439
|
options.sequential = this.sequential || !this.concurrent && (options == null ? void 0 : options.sequential);
|
384
|
-
const test3 = task(
|
385
|
-
|
386
|
-
|
387
|
-
|
440
|
+
const test3 = task(formatName(name2), {
|
441
|
+
...this,
|
442
|
+
...options,
|
443
|
+
handler
|
444
|
+
});
|
388
445
|
test3.type = "test";
|
389
446
|
});
|
390
447
|
const collector = {
|
@@ -403,8 +460,9 @@ function createSuiteCollector(name, factory = () => {
|
|
403
460
|
getHooks(suite2)[name2].push(...fn);
|
404
461
|
}
|
405
462
|
function initSuite(includeLocation) {
|
406
|
-
if (typeof suiteOptions === "number")
|
463
|
+
if (typeof suiteOptions === "number") {
|
407
464
|
suiteOptions = { timeout: suiteOptions };
|
465
|
+
}
|
408
466
|
suite2 = {
|
409
467
|
id: "",
|
410
468
|
type: "suite",
|
@@ -423,8 +481,9 @@ function createSuiteCollector(name, factory = () => {
|
|
423
481
|
const error = new Error("stacktrace").stack;
|
424
482
|
Error.stackTraceLimit = limit;
|
425
483
|
const stack = findTestFileStackTrace(error, suite2.each ?? false);
|
426
|
-
if (stack)
|
484
|
+
if (stack) {
|
427
485
|
suite2.location = stack;
|
486
|
+
}
|
428
487
|
}
|
429
488
|
setHooks(suite2, createSuiteHooks());
|
430
489
|
}
|
@@ -434,14 +493,17 @@ function createSuiteCollector(name, factory = () => {
|
|
434
493
|
initSuite(false);
|
435
494
|
}
|
436
495
|
async function collect(file) {
|
437
|
-
if (!file)
|
496
|
+
if (!file) {
|
438
497
|
throw new TypeError("File is required to collect tasks.");
|
498
|
+
}
|
439
499
|
factoryQueue.length = 0;
|
440
|
-
if (factory)
|
500
|
+
if (factory) {
|
441
501
|
await runWithSuite(collector, () => factory(test2));
|
502
|
+
}
|
442
503
|
const allChildren = [];
|
443
|
-
for (const i of [...factoryQueue, ...tasks])
|
504
|
+
for (const i of [...factoryQueue, ...tasks]) {
|
444
505
|
allChildren.push(i.type === "collector" ? await i.collect(file) : i);
|
506
|
+
}
|
445
507
|
suite2.file = file;
|
446
508
|
suite2.tasks = allChildren;
|
447
509
|
allChildren.forEach((task2) => {
|
@@ -461,31 +523,41 @@ function createSuite() {
|
|
461
523
|
factoryOrOptions,
|
462
524
|
optionsOrFactory
|
463
525
|
);
|
464
|
-
if (currentSuite == null ? void 0 : currentSuite.options)
|
526
|
+
if (currentSuite == null ? void 0 : currentSuite.options) {
|
465
527
|
options = { ...currentSuite.options, ...options };
|
528
|
+
}
|
466
529
|
const isConcurrent = options.concurrent || this.concurrent && !this.sequential;
|
467
530
|
const isSequential = options.sequential || this.sequential && !this.concurrent;
|
468
531
|
options.concurrent = isConcurrent && !isSequential;
|
469
532
|
options.sequential = isSequential && !isConcurrent;
|
470
|
-
return createSuiteCollector(
|
533
|
+
return createSuiteCollector(
|
534
|
+
formatName(name),
|
535
|
+
factory,
|
536
|
+
mode,
|
537
|
+
this.shuffle,
|
538
|
+
this.each,
|
539
|
+
options
|
540
|
+
);
|
471
541
|
}
|
472
542
|
suiteFn.each = function(cases, ...args) {
|
473
543
|
const suite2 = this.withContext();
|
474
544
|
this.setContext("each", true);
|
475
|
-
if (Array.isArray(cases) && args.length)
|
545
|
+
if (Array.isArray(cases) && args.length) {
|
476
546
|
cases = formatTemplateString(cases, args);
|
547
|
+
}
|
477
548
|
return (name, optionsOrFn, fnOrOptions) => {
|
478
549
|
const _name = formatName(name);
|
479
550
|
const arrayOnlyCases = cases.every(Array.isArray);
|
480
|
-
const { options, handler } = parseArguments(
|
481
|
-
optionsOrFn,
|
482
|
-
fnOrOptions
|
483
|
-
);
|
551
|
+
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
484
552
|
const fnFirst = typeof optionsOrFn === "function";
|
485
553
|
cases.forEach((i, idx) => {
|
486
554
|
const items = Array.isArray(i) ? i : [i];
|
487
555
|
if (fnFirst) {
|
488
|
-
arrayOnlyCases ? suite2(
|
556
|
+
arrayOnlyCases ? suite2(
|
557
|
+
formatTitle(_name, items, idx),
|
558
|
+
() => handler(...items),
|
559
|
+
options
|
560
|
+
) : suite2(formatTitle(_name, items, idx), () => handler(i), options);
|
489
561
|
} else {
|
490
562
|
arrayOnlyCases ? suite2(formatTitle(_name, items, idx), options, () => handler(...items)) : suite2(formatTitle(_name, items, idx), options, () => handler(i));
|
491
563
|
}
|
@@ -505,20 +577,22 @@ function createTaskCollector(fn, context) {
|
|
505
577
|
taskFn.each = function(cases, ...args) {
|
506
578
|
const test2 = this.withContext();
|
507
579
|
this.setContext("each", true);
|
508
|
-
if (Array.isArray(cases) && args.length)
|
580
|
+
if (Array.isArray(cases) && args.length) {
|
509
581
|
cases = formatTemplateString(cases, args);
|
582
|
+
}
|
510
583
|
return (name, optionsOrFn, fnOrOptions) => {
|
511
584
|
const _name = formatName(name);
|
512
585
|
const arrayOnlyCases = cases.every(Array.isArray);
|
513
|
-
const { options, handler } = parseArguments(
|
514
|
-
optionsOrFn,
|
515
|
-
fnOrOptions
|
516
|
-
);
|
586
|
+
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
517
587
|
const fnFirst = typeof optionsOrFn === "function";
|
518
588
|
cases.forEach((i, idx) => {
|
519
589
|
const items = Array.isArray(i) ? i : [i];
|
520
590
|
if (fnFirst) {
|
521
|
-
arrayOnlyCases ? test2(
|
591
|
+
arrayOnlyCases ? test2(
|
592
|
+
formatTitle(_name, items, idx),
|
593
|
+
() => handler(...items),
|
594
|
+
options
|
595
|
+
) : test2(formatTitle(_name, items, idx), () => handler(i), options);
|
522
596
|
} else {
|
523
597
|
arrayOnlyCases ? test2(formatTitle(_name, items, idx), options, () => handler(...items)) : test2(formatTitle(_name, items, idx), options, () => handler(i));
|
524
598
|
}
|
@@ -526,6 +600,22 @@ function createTaskCollector(fn, context) {
|
|
526
600
|
this.setContext("each", void 0);
|
527
601
|
};
|
528
602
|
};
|
603
|
+
taskFn.for = function(cases, ...args) {
|
604
|
+
const test2 = this.withContext();
|
605
|
+
if (Array.isArray(cases) && args.length) {
|
606
|
+
cases = formatTemplateString(cases, args);
|
607
|
+
}
|
608
|
+
return (name, optionsOrFn, fnOrOptions) => {
|
609
|
+
const _name = formatName(name);
|
610
|
+
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
611
|
+
cases.forEach((item, idx) => {
|
612
|
+
const handlerWrapper = (ctx) => handler(item, ctx);
|
613
|
+
handlerWrapper.__VITEST_FIXTURE_INDEX__ = 1;
|
614
|
+
handlerWrapper.toString = () => handler.toString();
|
615
|
+
test2(formatTitle(_name, toArray(item), idx), options, handlerWrapper);
|
616
|
+
});
|
617
|
+
};
|
618
|
+
};
|
529
619
|
taskFn.skipIf = function(condition) {
|
530
620
|
return condition ? this.skip : this;
|
531
621
|
};
|
@@ -535,15 +625,21 @@ function createTaskCollector(fn, context) {
|
|
535
625
|
taskFn.extend = function(fixtures) {
|
536
626
|
const _context = mergeContextFixtures(fixtures, context);
|
537
627
|
return createTest(function fn2(name, optionsOrFn, optionsOrTest) {
|
538
|
-
getCurrentSuite().test.fn.call(
|
628
|
+
getCurrentSuite().test.fn.call(
|
629
|
+
this,
|
630
|
+
formatName(name),
|
631
|
+
optionsOrFn,
|
632
|
+
optionsOrTest
|
633
|
+
);
|
539
634
|
}, _context);
|
540
635
|
};
|
541
636
|
const _test = createChainable(
|
542
637
|
["concurrent", "sequential", "skip", "only", "todo", "fails"],
|
543
638
|
taskFn
|
544
639
|
);
|
545
|
-
if (context)
|
640
|
+
if (context) {
|
546
641
|
_test.mergeContext(context);
|
642
|
+
}
|
547
643
|
return _test;
|
548
644
|
}
|
549
645
|
function createTest(fn, context) {
|
@@ -576,7 +672,9 @@ function formatTitle(template, items, idx) {
|
|
576
672
|
// https://github.com/chaijs/chai/pull/1490
|
577
673
|
(_, key) => {
|
578
674
|
var _a, _b;
|
579
|
-
return objDisplay(objectAttr(items[0], key), {
|
675
|
+
return objDisplay(objectAttr(items[0], key), {
|
676
|
+
truncate: (_b = (_a = runner == null ? void 0 : runner.config) == null ? void 0 : _a.chaiConfig) == null ? void 0 : _b.truncateThreshold
|
677
|
+
});
|
580
678
|
}
|
581
679
|
);
|
582
680
|
}
|
@@ -587,8 +685,9 @@ function formatTemplateString(cases, args) {
|
|
587
685
|
const res = [];
|
588
686
|
for (let i = 0; i < Math.floor(args.length / header.length); i++) {
|
589
687
|
const oneCase = {};
|
590
|
-
for (let j = 0; j < header.length; j++)
|
688
|
+
for (let j = 0; j < header.length; j++) {
|
591
689
|
oneCase[header[j]] = args[i * header.length + j];
|
690
|
+
}
|
592
691
|
res.push(oneCase);
|
593
692
|
}
|
594
693
|
return res;
|
@@ -621,8 +720,9 @@ async function runSetupFiles(config, runner) {
|
|
621
720
|
})
|
622
721
|
);
|
623
722
|
} else {
|
624
|
-
for (const fsPath of files)
|
723
|
+
for (const fsPath of files) {
|
625
724
|
await runner.importFile(fsPath, "setup");
|
725
|
+
}
|
626
726
|
}
|
627
727
|
}
|
628
728
|
|
@@ -632,7 +732,7 @@ async function collectTests(paths, runner) {
|
|
632
732
|
const files = [];
|
633
733
|
const config = runner.config;
|
634
734
|
for (const filepath of paths) {
|
635
|
-
const file = createFileTask(filepath, config.root, config.name);
|
735
|
+
const file = createFileTask(filepath, config.root, config.name, runner.pool);
|
636
736
|
(_a = runner.onCollectStart) == null ? void 0 : _a.call(runner, file);
|
637
737
|
clearCollectorContext(filepath, runner);
|
638
738
|
try {
|
@@ -668,11 +768,18 @@ async function collectTests(paths, runner) {
|
|
668
768
|
}
|
669
769
|
calculateSuiteHash(file);
|
670
770
|
const hasOnlyTasks = someTasksAreOnly(file);
|
671
|
-
interpretTaskModes(
|
771
|
+
interpretTaskModes(
|
772
|
+
file,
|
773
|
+
config.testNamePattern,
|
774
|
+
hasOnlyTasks,
|
775
|
+
false,
|
776
|
+
config.allowOnly
|
777
|
+
);
|
672
778
|
file.tasks.forEach((task) => {
|
673
779
|
var _a2;
|
674
|
-
if (((_a2 = task.suite) == null ? void 0 : _a2.id) === "")
|
780
|
+
if (((_a2 = task.suite) == null ? void 0 : _a2.id) === "") {
|
675
781
|
delete task.suite;
|
782
|
+
}
|
676
783
|
});
|
677
784
|
files.push(file);
|
678
785
|
}
|
@@ -689,10 +796,12 @@ function mergeHooks(baseHooks, hooks) {
|
|
689
796
|
const now = Date.now;
|
690
797
|
function updateSuiteHookState(suite, name, state, runner) {
|
691
798
|
var _a;
|
692
|
-
if (!suite.result)
|
799
|
+
if (!suite.result) {
|
693
800
|
suite.result = { state: "run" };
|
694
|
-
|
801
|
+
}
|
802
|
+
if (!((_a = suite.result) == null ? void 0 : _a.hooks)) {
|
695
803
|
suite.result.hooks = {};
|
804
|
+
}
|
696
805
|
const suiteHooks = suite.result.hooks;
|
697
806
|
if (suiteHooks) {
|
698
807
|
suiteHooks[name] = state;
|
@@ -701,18 +810,21 @@ function updateSuiteHookState(suite, name, state, runner) {
|
|
701
810
|
}
|
702
811
|
function getSuiteHooks(suite, name, sequence) {
|
703
812
|
const hooks = getHooks(suite)[name];
|
704
|
-
if (sequence === "stack" && (name === "afterAll" || name === "afterEach"))
|
813
|
+
if (sequence === "stack" && (name === "afterAll" || name === "afterEach")) {
|
705
814
|
return hooks.slice().reverse();
|
815
|
+
}
|
706
816
|
return hooks;
|
707
817
|
}
|
708
818
|
async function callTaskHooks(task, hooks, sequence) {
|
709
|
-
if (sequence === "stack")
|
819
|
+
if (sequence === "stack") {
|
710
820
|
hooks = hooks.slice().reverse();
|
821
|
+
}
|
711
822
|
if (sequence === "parallel") {
|
712
823
|
await Promise.all(hooks.map((fn) => fn(task.result)));
|
713
824
|
} else {
|
714
|
-
for (const fn of hooks)
|
825
|
+
for (const fn of hooks) {
|
715
826
|
await fn(task.result);
|
827
|
+
}
|
716
828
|
}
|
717
829
|
}
|
718
830
|
async function callSuiteHook(suite, currentTask, name, runner, args) {
|
@@ -727,10 +839,13 @@ async function callSuiteHook(suite, currentTask, name, runner, args) {
|
|
727
839
|
updateSuiteHookState(currentTask, name, "run", runner);
|
728
840
|
const hooks = getSuiteHooks(suite, name, sequence);
|
729
841
|
if (sequence === "parallel") {
|
730
|
-
callbacks.push(
|
842
|
+
callbacks.push(
|
843
|
+
...await Promise.all(hooks.map((fn) => fn(...args)))
|
844
|
+
);
|
731
845
|
} else {
|
732
|
-
for (const hook of hooks)
|
846
|
+
for (const hook of hooks) {
|
733
847
|
callbacks.push(await hook(...args));
|
848
|
+
}
|
734
849
|
}
|
735
850
|
updateSuiteHookState(currentTask, name, "pass", runner);
|
736
851
|
if (name === "afterEach" && parentSuite) {
|
@@ -758,11 +873,7 @@ async function sendTasksUpdate(runner) {
|
|
758
873
|
await previousUpdate;
|
759
874
|
if (packs.size) {
|
760
875
|
const taskPacks = Array.from(packs).map(([id, task]) => {
|
761
|
-
return [
|
762
|
-
id,
|
763
|
-
task[0],
|
764
|
-
task[1]
|
765
|
-
];
|
876
|
+
return [id, task[0], task[1]];
|
766
877
|
});
|
767
878
|
const p = (_a = runner.onTaskUpdate) == null ? void 0 : _a.call(runner, taskPacks);
|
768
879
|
packs.clear();
|
@@ -770,17 +881,21 @@ async function sendTasksUpdate(runner) {
|
|
770
881
|
}
|
771
882
|
}
|
772
883
|
async function callCleanupHooks(cleanups) {
|
773
|
-
await Promise.all(
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
884
|
+
await Promise.all(
|
885
|
+
cleanups.map(async (fn) => {
|
886
|
+
if (typeof fn !== "function") {
|
887
|
+
return;
|
888
|
+
}
|
889
|
+
await fn();
|
890
|
+
})
|
891
|
+
);
|
778
892
|
}
|
779
893
|
async function runTest(test, runner) {
|
780
|
-
var _a, _b, _c, _d, _e, _f;
|
894
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
781
895
|
await ((_a = runner.onBeforeRunTask) == null ? void 0 : _a.call(runner, test));
|
782
|
-
if (test.mode !== "run")
|
896
|
+
if (test.mode !== "run") {
|
783
897
|
return;
|
898
|
+
}
|
784
899
|
if (((_b = test.result) == null ? void 0 : _b.state) === "fail") {
|
785
900
|
updateTask(test, runner);
|
786
901
|
return;
|
@@ -800,29 +915,46 @@ async function runTest(test, runner) {
|
|
800
915
|
for (let retryCount = 0; retryCount <= retry; retryCount++) {
|
801
916
|
let beforeEachCleanups = [];
|
802
917
|
try {
|
803
|
-
await ((_c = runner.onBeforeTryTask) == null ? void 0 : _c.call(runner, test, {
|
918
|
+
await ((_c = runner.onBeforeTryTask) == null ? void 0 : _c.call(runner, test, {
|
919
|
+
retry: retryCount,
|
920
|
+
repeats: repeatCount
|
921
|
+
}));
|
804
922
|
test.result.repeatCount = repeatCount;
|
805
|
-
beforeEachCleanups = await callSuiteHook(
|
923
|
+
beforeEachCleanups = await callSuiteHook(
|
924
|
+
suite,
|
925
|
+
test,
|
926
|
+
"beforeEach",
|
927
|
+
runner,
|
928
|
+
[test.context, suite]
|
929
|
+
);
|
806
930
|
if (runner.runTask) {
|
807
931
|
await runner.runTask(test);
|
808
932
|
} else {
|
809
933
|
const fn = getFn(test);
|
810
|
-
if (!fn)
|
811
|
-
throw new Error(
|
934
|
+
if (!fn) {
|
935
|
+
throw new Error(
|
936
|
+
"Test function is not found. Did you add it using `setFn`?"
|
937
|
+
);
|
938
|
+
}
|
812
939
|
await fn();
|
813
940
|
}
|
814
941
|
if (test.promises) {
|
815
942
|
const result = await Promise.allSettled(test.promises);
|
816
943
|
const errors = result.map((r) => r.status === "rejected" ? r.reason : void 0).filter(Boolean);
|
817
|
-
if (errors.length)
|
944
|
+
if (errors.length) {
|
818
945
|
throw errors;
|
946
|
+
}
|
819
947
|
}
|
820
|
-
await ((_d = runner.onAfterTryTask) == null ? void 0 : _d.call(runner, test, {
|
948
|
+
await ((_d = runner.onAfterTryTask) == null ? void 0 : _d.call(runner, test, {
|
949
|
+
retry: retryCount,
|
950
|
+
repeats: repeatCount
|
951
|
+
}));
|
821
952
|
if (test.result.state !== "fail") {
|
822
|
-
if (!test.repeats)
|
953
|
+
if (!test.repeats) {
|
823
954
|
test.result.state = "pass";
|
824
|
-
else if (test.repeats && retry === retryCount)
|
955
|
+
} else if (test.repeats && retry === retryCount) {
|
825
956
|
test.result.state = "pass";
|
957
|
+
}
|
826
958
|
}
|
827
959
|
} catch (e) {
|
828
960
|
failTask(test.result, e, runner.config.diffOptions);
|
@@ -835,14 +967,23 @@ async function runTest(test, runner) {
|
|
835
967
|
return;
|
836
968
|
}
|
837
969
|
try {
|
838
|
-
await
|
970
|
+
await ((_f = runner.onTaskFinished) == null ? void 0 : _f.call(runner, test));
|
971
|
+
} catch (e) {
|
972
|
+
failTask(test.result, e, runner.config.diffOptions);
|
973
|
+
}
|
974
|
+
try {
|
975
|
+
await callSuiteHook(suite, test, "afterEach", runner, [
|
976
|
+
test.context,
|
977
|
+
suite
|
978
|
+
]);
|
839
979
|
await callCleanupHooks(beforeEachCleanups);
|
840
980
|
await callFixtureCleanup(test.context);
|
841
981
|
} catch (e) {
|
842
982
|
failTask(test.result, e, runner.config.diffOptions);
|
843
983
|
}
|
844
|
-
if (test.result.state === "pass")
|
984
|
+
if (test.result.state === "pass") {
|
845
985
|
break;
|
986
|
+
}
|
846
987
|
if (retryCount < retry) {
|
847
988
|
test.result.state = "run";
|
848
989
|
test.result.retryCount = (test.result.retryCount ?? 0) + 1;
|
@@ -857,7 +998,11 @@ async function runTest(test, runner) {
|
|
857
998
|
}
|
858
999
|
if (test.result.state === "fail") {
|
859
1000
|
try {
|
860
|
-
await callTaskHooks(
|
1001
|
+
await callTaskHooks(
|
1002
|
+
test,
|
1003
|
+
test.onFailed || [],
|
1004
|
+
runner.config.sequence.hooks
|
1005
|
+
);
|
861
1006
|
} catch (e) {
|
862
1007
|
failTask(test.result, e, runner.config.diffOptions);
|
863
1008
|
}
|
@@ -874,7 +1019,7 @@ async function runTest(test, runner) {
|
|
874
1019
|
}
|
875
1020
|
setCurrentTest(void 0);
|
876
1021
|
test.result.duration = now() - start;
|
877
|
-
await ((
|
1022
|
+
await ((_g = runner.onAfterRunTask) == null ? void 0 : _g.call(runner, test));
|
878
1023
|
updateTask(test, runner);
|
879
1024
|
}
|
880
1025
|
function failTask(result, err, diffOptions) {
|
@@ -895,8 +1040,9 @@ function markTasksAsSkipped(suite, runner) {
|
|
895
1040
|
t.mode = "skip";
|
896
1041
|
t.result = { ...t.result, state: "skip" };
|
897
1042
|
updateTask(t, runner);
|
898
|
-
if (t.type === "suite")
|
1043
|
+
if (t.type === "suite") {
|
899
1044
|
markTasksAsSkipped(t, runner);
|
1045
|
+
}
|
900
1046
|
});
|
901
1047
|
}
|
902
1048
|
async function runSuite(suite, runner) {
|
@@ -920,7 +1066,13 @@ async function runSuite(suite, runner) {
|
|
920
1066
|
suite.result.state = "todo";
|
921
1067
|
} else {
|
922
1068
|
try {
|
923
|
-
beforeAllCleanups = await callSuiteHook(
|
1069
|
+
beforeAllCleanups = await callSuiteHook(
|
1070
|
+
suite,
|
1071
|
+
suite,
|
1072
|
+
"beforeAll",
|
1073
|
+
runner,
|
1074
|
+
[suite]
|
1075
|
+
);
|
924
1076
|
if (runner.runSuite) {
|
925
1077
|
await runner.runSuite(suite);
|
926
1078
|
} else {
|
@@ -930,13 +1082,18 @@ async function runSuite(suite, runner) {
|
|
930
1082
|
} else {
|
931
1083
|
const { sequence } = runner.config;
|
932
1084
|
if (sequence.shuffle || suite.shuffle) {
|
933
|
-
const suites = tasksGroup.filter(
|
1085
|
+
const suites = tasksGroup.filter(
|
1086
|
+
(group) => group.type === "suite"
|
1087
|
+
);
|
934
1088
|
const tests = tasksGroup.filter((group) => group.type === "test");
|
935
1089
|
const groups = shuffle([suites, tests], sequence.seed);
|
936
|
-
tasksGroup = groups.flatMap(
|
1090
|
+
tasksGroup = groups.flatMap(
|
1091
|
+
(group) => shuffle(group, sequence.seed)
|
1092
|
+
);
|
937
1093
|
}
|
938
|
-
for (const c of tasksGroup)
|
1094
|
+
for (const c of tasksGroup) {
|
939
1095
|
await runSuiteChild(c, runner);
|
1096
|
+
}
|
940
1097
|
}
|
941
1098
|
}
|
942
1099
|
}
|
@@ -953,7 +1110,9 @@ async function runSuite(suite, runner) {
|
|
953
1110
|
if (!runner.config.passWithNoTests && !hasTests(suite)) {
|
954
1111
|
suite.result.state = "fail";
|
955
1112
|
if (!((_c = suite.result.errors) == null ? void 0 : _c.length)) {
|
956
|
-
const error = processError(
|
1113
|
+
const error = processError(
|
1114
|
+
new Error(`No test found in suite ${suite.name}`)
|
1115
|
+
);
|
957
1116
|
suite.result.errors = [error];
|
958
1117
|
}
|
959
1118
|
} else if (hasFailed(suite)) {
|
@@ -969,18 +1128,21 @@ async function runSuite(suite, runner) {
|
|
969
1128
|
}
|
970
1129
|
let limitMaxConcurrency;
|
971
1130
|
async function runSuiteChild(c, runner) {
|
972
|
-
if (c.type === "test" || c.type === "custom")
|
1131
|
+
if (c.type === "test" || c.type === "custom") {
|
973
1132
|
return limitMaxConcurrency(() => runTest(c, runner));
|
974
|
-
else if (c.type === "suite")
|
1133
|
+
} else if (c.type === "suite") {
|
975
1134
|
return runSuite(c, runner);
|
1135
|
+
}
|
976
1136
|
}
|
977
1137
|
async function runFiles(files, runner) {
|
978
1138
|
var _a, _b;
|
979
|
-
limitMaxConcurrency ?? (limitMaxConcurrency =
|
1139
|
+
limitMaxConcurrency ?? (limitMaxConcurrency = limitConcurrency(runner.config.maxConcurrency));
|
980
1140
|
for (const file of files) {
|
981
1141
|
if (!file.tasks.length && !runner.config.passWithNoTests) {
|
982
1142
|
if (!((_b = (_a = file.result) == null ? void 0 : _a.errors) == null ? void 0 : _b.length)) {
|
983
|
-
const error = processError(
|
1143
|
+
const error = processError(
|
1144
|
+
new Error(`No test suite found in file ${file.filepath}`)
|
1145
|
+
);
|
984
1146
|
file.result = {
|
985
1147
|
state: "fail",
|
986
1148
|
errors: [error]
|
@@ -1001,37 +1163,63 @@ async function startTests(paths, runner) {
|
|
1001
1163
|
await sendTasksUpdate(runner);
|
1002
1164
|
return files;
|
1003
1165
|
}
|
1166
|
+
async function publicCollect(paths, runner) {
|
1167
|
+
var _a, _b;
|
1168
|
+
await ((_a = runner.onBeforeCollect) == null ? void 0 : _a.call(runner, paths));
|
1169
|
+
const files = await collectTests(paths, runner);
|
1170
|
+
await ((_b = runner.onCollected) == null ? void 0 : _b.call(runner, files));
|
1171
|
+
return files;
|
1172
|
+
}
|
1004
1173
|
|
1005
1174
|
function getDefaultHookTimeout() {
|
1006
1175
|
return getRunner().config.hookTimeout;
|
1007
1176
|
}
|
1008
1177
|
function beforeAll(fn, timeout) {
|
1009
|
-
return getCurrentSuite().on(
|
1178
|
+
return getCurrentSuite().on(
|
1179
|
+
"beforeAll",
|
1180
|
+
withTimeout(fn, timeout ?? getDefaultHookTimeout(), true)
|
1181
|
+
);
|
1010
1182
|
}
|
1011
1183
|
function afterAll(fn, timeout) {
|
1012
|
-
return getCurrentSuite().on(
|
1184
|
+
return getCurrentSuite().on(
|
1185
|
+
"afterAll",
|
1186
|
+
withTimeout(fn, timeout ?? getDefaultHookTimeout(), true)
|
1187
|
+
);
|
1013
1188
|
}
|
1014
1189
|
function beforeEach(fn, timeout) {
|
1015
|
-
return getCurrentSuite().on(
|
1190
|
+
return getCurrentSuite().on(
|
1191
|
+
"beforeEach",
|
1192
|
+
withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true)
|
1193
|
+
);
|
1016
1194
|
}
|
1017
1195
|
function afterEach(fn, timeout) {
|
1018
|
-
return getCurrentSuite().on(
|
1196
|
+
return getCurrentSuite().on(
|
1197
|
+
"afterEach",
|
1198
|
+
withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true)
|
1199
|
+
);
|
1019
1200
|
}
|
1020
|
-
const onTestFailed = createTestHook(
|
1021
|
-
|
1022
|
-
test
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1201
|
+
const onTestFailed = createTestHook(
|
1202
|
+
"onTestFailed",
|
1203
|
+
(test, handler) => {
|
1204
|
+
test.onFailed || (test.onFailed = []);
|
1205
|
+
test.onFailed.push(handler);
|
1206
|
+
}
|
1207
|
+
);
|
1208
|
+
const onTestFinished = createTestHook(
|
1209
|
+
"onTestFinished",
|
1210
|
+
(test, handler) => {
|
1211
|
+
test.onFinished || (test.onFinished = []);
|
1212
|
+
test.onFinished.push(handler);
|
1213
|
+
}
|
1214
|
+
);
|
1028
1215
|
function createTestHook(name, handler) {
|
1029
1216
|
return (fn) => {
|
1030
1217
|
const current = getCurrentTest();
|
1031
|
-
if (!current)
|
1218
|
+
if (!current) {
|
1032
1219
|
throw new Error(`Hook ${name}() can only be called inside a test`);
|
1220
|
+
}
|
1033
1221
|
return handler(current, fn);
|
1034
1222
|
};
|
1035
1223
|
}
|
1036
1224
|
|
1037
|
-
export { afterAll, afterEach, beforeAll, beforeEach, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };
|
1225
|
+
export { afterAll, afterEach, beforeAll, beforeEach, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };
|