@vitest/runner 2.0.0-beta.1 → 2.0.0-beta.11
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 +64 -22
- package/dist/index.d.ts +4 -4
- package/dist/index.js +355 -172
- package/dist/{tasks-Ck0GpLiZ.d.ts → tasks-BP89OzIP.d.ts} +25 -6
- package/dist/types.d.ts +7 -3
- package/dist/utils.d.ts +5 -3
- package/dist/utils.js +2 -1
- package/package.json +2 -2
package/dist/chunk-tasks.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { processError } from '@vitest/utils/error';
|
2
|
+
import { relative } from 'pathe';
|
2
3
|
import { toArray } from '@vitest/utils';
|
3
4
|
|
4
5
|
function partitionSuiteChildren(suite) {
|
@@ -12,8 +13,9 @@ function partitionSuiteChildren(suite) {
|
|
12
13
|
tasksGroup = [c];
|
13
14
|
}
|
14
15
|
}
|
15
|
-
if (tasksGroup.length > 0)
|
16
|
+
if (tasksGroup.length > 0) {
|
16
17
|
tasksGroups.push(tasksGroup);
|
18
|
+
}
|
17
19
|
return tasksGroups;
|
18
20
|
}
|
19
21
|
|
@@ -35,39 +37,50 @@ function interpretTaskModes(suite, namePattern, onlyMode, parentIsOnly, allowOnl
|
|
35
37
|
}
|
36
38
|
}
|
37
39
|
if (t.type === "test") {
|
38
|
-
if (namePattern && !getTaskFullName(t).match(namePattern))
|
40
|
+
if (namePattern && !getTaskFullName(t).match(namePattern)) {
|
39
41
|
t.mode = "skip";
|
42
|
+
}
|
40
43
|
} else if (t.type === "suite") {
|
41
|
-
if (t.mode === "skip")
|
44
|
+
if (t.mode === "skip") {
|
42
45
|
skipAllTasks(t);
|
43
|
-
else
|
46
|
+
} else {
|
44
47
|
interpretTaskModes(t, namePattern, onlyMode, includeTask, allowOnly);
|
48
|
+
}
|
45
49
|
}
|
46
50
|
});
|
47
51
|
if (suite.mode === "run") {
|
48
|
-
if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run"))
|
52
|
+
if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run")) {
|
49
53
|
suite.mode = "skip";
|
54
|
+
}
|
50
55
|
}
|
51
56
|
}
|
52
57
|
function getTaskFullName(task) {
|
53
58
|
return `${task.suite ? `${getTaskFullName(task.suite)} ` : ""}${task.name}`;
|
54
59
|
}
|
55
60
|
function someTasksAreOnly(suite) {
|
56
|
-
return suite.tasks.some(
|
61
|
+
return suite.tasks.some(
|
62
|
+
(t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t)
|
63
|
+
);
|
57
64
|
}
|
58
65
|
function skipAllTasks(suite) {
|
59
66
|
suite.tasks.forEach((t) => {
|
60
67
|
if (t.mode === "run") {
|
61
68
|
t.mode = "skip";
|
62
|
-
if (t.type === "suite")
|
69
|
+
if (t.type === "suite") {
|
63
70
|
skipAllTasks(t);
|
71
|
+
}
|
64
72
|
}
|
65
73
|
});
|
66
74
|
}
|
67
75
|
function checkAllowOnly(task, allowOnly) {
|
68
|
-
if (allowOnly)
|
76
|
+
if (allowOnly) {
|
69
77
|
return;
|
70
|
-
|
78
|
+
}
|
79
|
+
const error = processError(
|
80
|
+
new Error(
|
81
|
+
"[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error"
|
82
|
+
)
|
83
|
+
);
|
71
84
|
task.result = {
|
72
85
|
state: "fail",
|
73
86
|
errors: [error]
|
@@ -75,8 +88,9 @@ function checkAllowOnly(task, allowOnly) {
|
|
75
88
|
}
|
76
89
|
function generateHash(str) {
|
77
90
|
let hash = 0;
|
78
|
-
if (str.length === 0)
|
91
|
+
if (str.length === 0) {
|
79
92
|
return `${hash}`;
|
93
|
+
}
|
80
94
|
for (let i = 0; i < str.length; i++) {
|
81
95
|
const char = str.charCodeAt(i);
|
82
96
|
hash = (hash << 5) - hash + char;
|
@@ -87,10 +101,27 @@ function generateHash(str) {
|
|
87
101
|
function calculateSuiteHash(parent) {
|
88
102
|
parent.tasks.forEach((t, idx) => {
|
89
103
|
t.id = `${parent.id}_${idx}`;
|
90
|
-
if (t.type === "suite")
|
104
|
+
if (t.type === "suite") {
|
91
105
|
calculateSuiteHash(t);
|
106
|
+
}
|
92
107
|
});
|
93
108
|
}
|
109
|
+
function createFileTask(filepath, root, projectName) {
|
110
|
+
const path = relative(root, filepath);
|
111
|
+
const file = {
|
112
|
+
id: generateHash(`${path}${projectName || ""}`),
|
113
|
+
name: path,
|
114
|
+
type: "suite",
|
115
|
+
mode: "run",
|
116
|
+
filepath,
|
117
|
+
tasks: [],
|
118
|
+
meta: /* @__PURE__ */ Object.create(null),
|
119
|
+
projectName,
|
120
|
+
file: void 0
|
121
|
+
};
|
122
|
+
file.file = file;
|
123
|
+
return file;
|
124
|
+
}
|
94
125
|
|
95
126
|
function createChainable(keys, fn) {
|
96
127
|
function create(context) {
|
@@ -134,8 +165,9 @@ function getTests(suite) {
|
|
134
165
|
tests.push(task);
|
135
166
|
} else {
|
136
167
|
const taskTests = getTests(task);
|
137
|
-
for (const test of taskTests)
|
168
|
+
for (const test of taskTests) {
|
138
169
|
tests.push(test);
|
170
|
+
}
|
139
171
|
}
|
140
172
|
}
|
141
173
|
}
|
@@ -143,31 +175,41 @@ function getTests(suite) {
|
|
143
175
|
return tests;
|
144
176
|
}
|
145
177
|
function getTasks(tasks = []) {
|
146
|
-
return toArray(tasks).flatMap(
|
178
|
+
return toArray(tasks).flatMap(
|
179
|
+
(s) => isAtomTest(s) ? [s] : [s, ...getTasks(s.tasks)]
|
180
|
+
);
|
147
181
|
}
|
148
182
|
function getSuites(suite) {
|
149
|
-
return toArray(suite).flatMap(
|
183
|
+
return toArray(suite).flatMap(
|
184
|
+
(s) => s.type === "suite" ? [s, ...getSuites(s.tasks)] : []
|
185
|
+
);
|
150
186
|
}
|
151
187
|
function hasTests(suite) {
|
152
|
-
return toArray(suite).some(
|
188
|
+
return toArray(suite).some(
|
189
|
+
(s) => s.tasks.some((c) => isAtomTest(c) || hasTests(c))
|
190
|
+
);
|
153
191
|
}
|
154
192
|
function hasFailed(suite) {
|
155
|
-
return toArray(suite).some(
|
156
|
-
|
157
|
-
|
158
|
-
|
193
|
+
return toArray(suite).some(
|
194
|
+
(s) => {
|
195
|
+
var _a;
|
196
|
+
return ((_a = s.result) == null ? void 0 : _a.state) === "fail" || s.type === "suite" && hasFailed(s.tasks);
|
197
|
+
}
|
198
|
+
);
|
159
199
|
}
|
160
200
|
function getNames(task) {
|
161
201
|
const names = [task.name];
|
162
202
|
let current = task;
|
163
203
|
while (current == null ? void 0 : current.suite) {
|
164
204
|
current = current.suite;
|
165
|
-
if (current == null ? void 0 : current.name)
|
205
|
+
if (current == null ? void 0 : current.name) {
|
166
206
|
names.unshift(current.name);
|
207
|
+
}
|
167
208
|
}
|
168
|
-
if (current !== task.file)
|
209
|
+
if (current !== task.file) {
|
169
210
|
names.unshift(task.file.name);
|
211
|
+
}
|
170
212
|
return names;
|
171
213
|
}
|
172
214
|
|
173
|
-
export {
|
215
|
+
export { createFileTask as a, isAtomTest as b, calculateSuiteHash as c, getTests as d, getTasks as e, getSuites as f, generateHash as g, hasTests as h, interpretTaskModes as i, hasFailed as j, getNames as k, createChainable as l, partitionSuiteChildren as p, someTasksAreOnly as s };
|
package/dist/index.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { VitestRunner } from './types.js';
|
2
2
|
export { CancelReason, VitestRunnerConfig, VitestRunnerConstructor, VitestRunnerImportSource } from './types.js';
|
3
|
-
import { T as Task, F as File, d as SuiteAPI, e as TestAPI, f as SuiteCollector, g as CustomAPI, h as SuiteHooks, O as OnTestFailedHandler, i as OnTestFinishedHandler, a as Test, C as Custom, S as Suite } from './tasks-
|
4
|
-
export { D as DoneCallback, E as ExtendedContext, t as Fixture, s as FixtureFn, r as FixtureOptions, u as Fixtures, v as HookCleanupCallback, H as HookListener, I as InferFixturesTypes, R as RunMode, y as RuntimeContext, B as SequenceHooks, G as SequenceSetupFiles, x as SuiteFactory, k as TaskBase, A as TaskContext, w as TaskCustomOptions, m as TaskMeta, l as TaskPopulated, n as TaskResult, o as TaskResultPack, j as TaskState, z as TestContext, p as TestFunction, q as TestOptions, U as Use } from './tasks-
|
3
|
+
import { T as Task, F as File, d as SuiteAPI, e as TestAPI, f as SuiteCollector, g as CustomAPI, h as SuiteHooks, O as OnTestFailedHandler, i as OnTestFinishedHandler, a as Test, C as Custom, S as Suite } from './tasks-BP89OzIP.js';
|
4
|
+
export { D as DoneCallback, E as ExtendedContext, t as Fixture, s as FixtureFn, r as FixtureOptions, u as Fixtures, v as HookCleanupCallback, H as HookListener, I as InferFixturesTypes, R as RunMode, y as RuntimeContext, B as SequenceHooks, G as SequenceSetupFiles, x as SuiteFactory, k as TaskBase, A as TaskContext, w as TaskCustomOptions, m as TaskMeta, l as TaskPopulated, n as TaskResult, o as TaskResultPack, j as TaskState, z as TestContext, p as TestFunction, q as TestOptions, U as Use } from './tasks-BP89OzIP.js';
|
5
5
|
import { Awaitable } from '@vitest/utils';
|
6
6
|
export { processError } from '@vitest/utils/error';
|
7
7
|
import '@vitest/utils/diff';
|
@@ -23,8 +23,8 @@ declare function afterEach<ExtraContext = {}>(fn: SuiteHooks<ExtraContext>['afte
|
|
23
23
|
declare const onTestFailed: (fn: OnTestFailedHandler) => void;
|
24
24
|
declare const onTestFinished: (fn: OnTestFinishedHandler) => void;
|
25
25
|
|
26
|
-
declare function setFn(key: Test | Custom, fn: (
|
27
|
-
declare function getFn<Task = Test | Custom>(key: Task): (
|
26
|
+
declare function setFn(key: Test | Custom, fn: () => Awaitable<void>): void;
|
27
|
+
declare function getFn<Task = Test | Custom>(key: Task): () => Awaitable<void>;
|
28
28
|
declare function setHooks(key: Suite, hooks: SuiteHooks): void;
|
29
29
|
declare function getHooks(key: Suite): SuiteHooks;
|
30
30
|
|
package/dist/index.js
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import limit from 'p-limit';
|
2
|
-
import { getSafeTimers, isObject, createDefer, format, objDisplay, objectAttr, toArray, shuffle } from '@vitest/utils';
|
2
|
+
import { getSafeTimers, isObject, createDefer, isNegativeNaN, format, objDisplay, objectAttr, toArray, shuffle } from '@vitest/utils';
|
3
3
|
import { processError } from '@vitest/utils/error';
|
4
4
|
export { processError } from '@vitest/utils/error';
|
5
|
-
import {
|
6
|
-
import { relative } from 'pathe';
|
5
|
+
import { l as createChainable, a as createFileTask, c as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, p as partitionSuiteChildren, h as hasTests, j as hasFailed } from './chunk-tasks.js';
|
7
6
|
import { parseSingleStack } from '@vitest/utils/source-map';
|
7
|
+
import 'pathe';
|
8
8
|
|
9
9
|
const fnMap = /* @__PURE__ */ new WeakMap();
|
10
10
|
const fixtureMap = /* @__PURE__ */ new WeakMap();
|
@@ -53,18 +53,22 @@ async function runWithSuite(suite, fn) {
|
|
53
53
|
collectorContext.currentSuite = prev;
|
54
54
|
}
|
55
55
|
function withTimeout(fn, timeout, isHook = false) {
|
56
|
-
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY)
|
56
|
+
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY) {
|
57
57
|
return fn;
|
58
|
+
}
|
58
59
|
const { setTimeout, clearTimeout } = getSafeTimers();
|
59
60
|
return (...args) => {
|
60
|
-
return Promise.race([
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
return Promise.race([
|
62
|
+
fn(...args),
|
63
|
+
new Promise((resolve, reject) => {
|
64
|
+
var _a;
|
65
|
+
const timer = setTimeout(() => {
|
66
|
+
clearTimeout(timer);
|
67
|
+
reject(new Error(makeTimeoutMsg(isHook, timeout)));
|
68
|
+
}, timeout);
|
69
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
70
|
+
})
|
71
|
+
]);
|
68
72
|
};
|
69
73
|
}
|
70
74
|
function createTestContext(test, runner) {
|
@@ -94,25 +98,31 @@ If this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as t
|
|
94
98
|
|
95
99
|
function mergeContextFixtures(fixtures, context = {}) {
|
96
100
|
const fixtureOptionKeys = ["auto"];
|
97
|
-
const fixtureArray = Object.entries(fixtures).map(
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
101
|
+
const fixtureArray = Object.entries(fixtures).map(
|
102
|
+
([prop, value]) => {
|
103
|
+
const fixtureItem = { value };
|
104
|
+
if (Array.isArray(value) && value.length >= 2 && isObject(value[1]) && Object.keys(value[1]).some((key) => fixtureOptionKeys.includes(key))) {
|
105
|
+
Object.assign(fixtureItem, value[1]);
|
106
|
+
fixtureItem.value = value[0];
|
107
|
+
}
|
108
|
+
fixtureItem.prop = prop;
|
109
|
+
fixtureItem.isFn = typeof fixtureItem.value === "function";
|
110
|
+
return fixtureItem;
|
102
111
|
}
|
103
|
-
|
104
|
-
|
105
|
-
return fixtureItem;
|
106
|
-
});
|
107
|
-
if (Array.isArray(context.fixtures))
|
112
|
+
);
|
113
|
+
if (Array.isArray(context.fixtures)) {
|
108
114
|
context.fixtures = context.fixtures.concat(fixtureArray);
|
109
|
-
else
|
115
|
+
} else {
|
110
116
|
context.fixtures = fixtureArray;
|
117
|
+
}
|
111
118
|
fixtureArray.forEach((fixture) => {
|
112
119
|
if (fixture.isFn) {
|
113
120
|
const usedProps = getUsedProps(fixture.value);
|
114
|
-
if (usedProps.length)
|
115
|
-
fixture.deps = context.fixtures.filter(
|
121
|
+
if (usedProps.length) {
|
122
|
+
fixture.deps = context.fixtures.filter(
|
123
|
+
({ prop }) => prop !== fixture.prop && usedProps.includes(prop)
|
124
|
+
);
|
125
|
+
}
|
116
126
|
}
|
117
127
|
});
|
118
128
|
return context;
|
@@ -121,36 +131,46 @@ const fixtureValueMaps = /* @__PURE__ */ new Map();
|
|
121
131
|
const cleanupFnArrayMap = /* @__PURE__ */ new Map();
|
122
132
|
async function callFixtureCleanup(context) {
|
123
133
|
const cleanupFnArray = cleanupFnArrayMap.get(context) ?? [];
|
124
|
-
for (const cleanup of cleanupFnArray.reverse())
|
134
|
+
for (const cleanup of cleanupFnArray.reverse()) {
|
125
135
|
await cleanup();
|
136
|
+
}
|
126
137
|
cleanupFnArrayMap.delete(context);
|
127
138
|
}
|
128
139
|
function withFixtures(fn, testContext) {
|
129
140
|
return (hookContext) => {
|
130
141
|
const context = hookContext || testContext;
|
131
|
-
if (!context)
|
142
|
+
if (!context) {
|
132
143
|
return fn({});
|
144
|
+
}
|
133
145
|
const fixtures = getFixture(context);
|
134
|
-
if (!(fixtures == null ? void 0 : fixtures.length))
|
146
|
+
if (!(fixtures == null ? void 0 : fixtures.length)) {
|
135
147
|
return fn(context);
|
148
|
+
}
|
136
149
|
const usedProps = getUsedProps(fn);
|
137
150
|
const hasAutoFixture = fixtures.some(({ auto }) => auto);
|
138
|
-
if (!usedProps.length && !hasAutoFixture)
|
151
|
+
if (!usedProps.length && !hasAutoFixture) {
|
139
152
|
return fn(context);
|
140
|
-
|
153
|
+
}
|
154
|
+
if (!fixtureValueMaps.get(context)) {
|
141
155
|
fixtureValueMaps.set(context, /* @__PURE__ */ new Map());
|
156
|
+
}
|
142
157
|
const fixtureValueMap = fixtureValueMaps.get(context);
|
143
|
-
if (!cleanupFnArrayMap.has(context))
|
158
|
+
if (!cleanupFnArrayMap.has(context)) {
|
144
159
|
cleanupFnArrayMap.set(context, []);
|
160
|
+
}
|
145
161
|
const cleanupFnArray = cleanupFnArrayMap.get(context);
|
146
|
-
const usedFixtures = fixtures.filter(
|
162
|
+
const usedFixtures = fixtures.filter(
|
163
|
+
({ prop, auto }) => auto || usedProps.includes(prop)
|
164
|
+
);
|
147
165
|
const pendingFixtures = resolveDeps(usedFixtures);
|
148
|
-
if (!pendingFixtures.length)
|
166
|
+
if (!pendingFixtures.length) {
|
149
167
|
return fn(context);
|
168
|
+
}
|
150
169
|
async function resolveFixtures() {
|
151
170
|
for (const fixture of pendingFixtures) {
|
152
|
-
if (fixtureValueMap.has(fixture))
|
171
|
+
if (fixtureValueMap.has(fixture)) {
|
153
172
|
continue;
|
173
|
+
}
|
154
174
|
const resolvedValue = fixture.isFn ? await resolveFixtureFunction(fixture.value, context, cleanupFnArray) : fixture.value;
|
155
175
|
context[fixture.prop] = resolvedValue;
|
156
176
|
fixtureValueMap.set(fixture, resolvedValue);
|
@@ -185,14 +205,18 @@ async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
|
|
185
205
|
}
|
186
206
|
function resolveDeps(fixtures, depSet = /* @__PURE__ */ new Set(), pendingFixtures = []) {
|
187
207
|
fixtures.forEach((fixture) => {
|
188
|
-
if (pendingFixtures.includes(fixture))
|
208
|
+
if (pendingFixtures.includes(fixture)) {
|
189
209
|
return;
|
210
|
+
}
|
190
211
|
if (!fixture.isFn || !fixture.deps) {
|
191
212
|
pendingFixtures.push(fixture);
|
192
213
|
return;
|
193
214
|
}
|
194
|
-
if (depSet.has(fixture))
|
195
|
-
throw new Error(
|
215
|
+
if (depSet.has(fixture)) {
|
216
|
+
throw new Error(
|
217
|
+
`Circular fixture dependency detected: ${fixture.prop} <- ${[...depSet].reverse().map((d) => d.prop).join(" <- ")}`
|
218
|
+
);
|
219
|
+
}
|
196
220
|
depSet.add(fixture);
|
197
221
|
resolveDeps(fixture.deps, depSet, pendingFixtures);
|
198
222
|
pendingFixtures.push(fixture);
|
@@ -202,21 +226,35 @@ function resolveDeps(fixtures, depSet = /* @__PURE__ */ new Set(), pendingFixtur
|
|
202
226
|
}
|
203
227
|
function getUsedProps(fn) {
|
204
228
|
const match = fn.toString().match(/[^(]*\(([^)]*)/);
|
205
|
-
if (!match)
|
229
|
+
if (!match) {
|
206
230
|
return [];
|
231
|
+
}
|
207
232
|
const args = splitByComma(match[1]);
|
208
|
-
if (!args.length)
|
233
|
+
if (!args.length) {
|
209
234
|
return [];
|
210
|
-
|
211
|
-
|
212
|
-
|
235
|
+
}
|
236
|
+
let first = args[0];
|
237
|
+
if ("__VITEST_FIXTURE_INDEX__" in fn) {
|
238
|
+
first = args[fn.__VITEST_FIXTURE_INDEX__];
|
239
|
+
if (!first) {
|
240
|
+
return [];
|
241
|
+
}
|
242
|
+
}
|
243
|
+
if (!(first.startsWith("{") && first.endsWith("}"))) {
|
244
|
+
throw new Error(
|
245
|
+
`The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "${first}".`
|
246
|
+
);
|
247
|
+
}
|
213
248
|
const _first = first.slice(1, -1).replace(/\s/g, "");
|
214
249
|
const props = splitByComma(_first).map((prop) => {
|
215
|
-
return prop.replace(
|
250
|
+
return prop.replace(/:.*|=.*/g, "");
|
216
251
|
});
|
217
252
|
const last = props.at(-1);
|
218
|
-
if (last && last.startsWith("..."))
|
219
|
-
throw new Error(
|
253
|
+
if (last && last.startsWith("...")) {
|
254
|
+
throw new Error(
|
255
|
+
`Rest parameters are not supported in fixtures, received "${last}".`
|
256
|
+
);
|
257
|
+
}
|
220
258
|
return props;
|
221
259
|
}
|
222
260
|
function splitByComma(s) {
|
@@ -230,14 +268,16 @@ function splitByComma(s) {
|
|
230
268
|
stack.pop();
|
231
269
|
} else if (!stack.length && s[i] === ",") {
|
232
270
|
const token = s.substring(start, i).trim();
|
233
|
-
if (token)
|
271
|
+
if (token) {
|
234
272
|
result.push(token);
|
273
|
+
}
|
235
274
|
start = i + 1;
|
236
275
|
}
|
237
276
|
}
|
238
277
|
const lastToken = s.substring(start).trim();
|
239
|
-
if (lastToken)
|
278
|
+
if (lastToken) {
|
240
279
|
result.push(lastToken);
|
280
|
+
}
|
241
281
|
return result;
|
242
282
|
}
|
243
283
|
|
@@ -250,13 +290,19 @@ function getCurrentTest() {
|
|
250
290
|
}
|
251
291
|
|
252
292
|
const suite = createSuite();
|
253
|
-
const test = createTest(
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
293
|
+
const test = createTest(function(name, optionsOrFn, optionsOrTest) {
|
294
|
+
if (getCurrentTest()) {
|
295
|
+
throw new Error(
|
296
|
+
'Calling the test function inside another test function is not allowed. Please put it inside "describe" or "suite" so it can be properly collected.'
|
297
|
+
);
|
258
298
|
}
|
259
|
-
)
|
299
|
+
getCurrentSuite().test.fn.call(
|
300
|
+
this,
|
301
|
+
formatName(name),
|
302
|
+
optionsOrFn,
|
303
|
+
optionsOrTest
|
304
|
+
);
|
305
|
+
});
|
260
306
|
const describe = suite;
|
261
307
|
const it = test;
|
262
308
|
let runner;
|
@@ -278,8 +324,9 @@ function createDefaultSuite(runner2) {
|
|
278
324
|
});
|
279
325
|
}
|
280
326
|
function clearCollectorContext(filepath, currentRunner) {
|
281
|
-
if (!defaultSuite)
|
327
|
+
if (!defaultSuite) {
|
282
328
|
defaultSuite = createDefaultSuite(currentRunner);
|
329
|
+
}
|
283
330
|
runner = currentRunner;
|
284
331
|
currentTestFilepath = filepath;
|
285
332
|
collectorContext.tasks.length = 0;
|
@@ -302,8 +349,11 @@ function parseArguments(optionsOrFn, optionsOrTest) {
|
|
302
349
|
let fn = () => {
|
303
350
|
};
|
304
351
|
if (typeof optionsOrTest === "object") {
|
305
|
-
if (typeof optionsOrFn === "object")
|
306
|
-
throw new TypeError(
|
352
|
+
if (typeof optionsOrFn === "object") {
|
353
|
+
throw new TypeError(
|
354
|
+
"Cannot use two objects as arguments. Please provide options and a function callback in that order."
|
355
|
+
);
|
356
|
+
}
|
307
357
|
options = optionsOrTest;
|
308
358
|
} else if (typeof optionsOrTest === "number") {
|
309
359
|
options = { timeout: optionsOrTest };
|
@@ -311,8 +361,11 @@ function parseArguments(optionsOrFn, optionsOrTest) {
|
|
311
361
|
options = optionsOrFn;
|
312
362
|
}
|
313
363
|
if (typeof optionsOrFn === "function") {
|
314
|
-
if (typeof optionsOrTest === "function")
|
315
|
-
throw new TypeError(
|
364
|
+
if (typeof optionsOrTest === "function") {
|
365
|
+
throw new TypeError(
|
366
|
+
"Cannot use two functions as arguments. Please use the second argument for options."
|
367
|
+
);
|
368
|
+
}
|
316
369
|
fn = optionsOrFn;
|
317
370
|
} else if (typeof optionsOrTest === "function") {
|
318
371
|
fn = optionsOrTest;
|
@@ -344,10 +397,12 @@ function createSuiteCollector(name, factory = () => {
|
|
344
397
|
meta: options.meta ?? /* @__PURE__ */ Object.create(null)
|
345
398
|
};
|
346
399
|
const handler = options.handler;
|
347
|
-
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent)
|
400
|
+
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent) {
|
348
401
|
task2.concurrent = true;
|
349
|
-
|
402
|
+
}
|
403
|
+
if (shuffle) {
|
350
404
|
task2.shuffle = true;
|
405
|
+
}
|
351
406
|
const context = createTestContext(task2, runner);
|
352
407
|
Object.defineProperty(task2, "context", {
|
353
408
|
value: context,
|
@@ -355,10 +410,13 @@ function createSuiteCollector(name, factory = () => {
|
|
355
410
|
});
|
356
411
|
setFixture(context, options.fixtures);
|
357
412
|
if (handler) {
|
358
|
-
setFn(
|
359
|
-
|
360
|
-
(
|
361
|
-
|
413
|
+
setFn(
|
414
|
+
task2,
|
415
|
+
withTimeout(
|
416
|
+
withFixtures(handler, context),
|
417
|
+
(options == null ? void 0 : options.timeout) ?? runner.config.testTimeout
|
418
|
+
)
|
419
|
+
);
|
362
420
|
}
|
363
421
|
if (runner.config.includeTaskLocation) {
|
364
422
|
const limit = Error.stackTraceLimit;
|
@@ -366,25 +424,25 @@ function createSuiteCollector(name, factory = () => {
|
|
366
424
|
const error = new Error("stacktrace").stack;
|
367
425
|
Error.stackTraceLimit = limit;
|
368
426
|
const stack = findTestFileStackTrace(error, task2.each ?? false);
|
369
|
-
if (stack)
|
427
|
+
if (stack) {
|
370
428
|
task2.location = stack;
|
429
|
+
}
|
371
430
|
}
|
372
431
|
tasks.push(task2);
|
373
432
|
return task2;
|
374
433
|
};
|
375
434
|
const test2 = createTest(function(name2, optionsOrFn, optionsOrTest) {
|
376
|
-
let { options, handler } = parseArguments(
|
377
|
-
|
378
|
-
optionsOrTest
|
379
|
-
);
|
380
|
-
if (typeof suiteOptions === "object")
|
435
|
+
let { options, handler } = parseArguments(optionsOrFn, optionsOrTest);
|
436
|
+
if (typeof suiteOptions === "object") {
|
381
437
|
options = Object.assign({}, suiteOptions, options);
|
438
|
+
}
|
382
439
|
options.concurrent = this.concurrent || !this.sequential && (options == null ? void 0 : options.concurrent);
|
383
440
|
options.sequential = this.sequential || !this.concurrent && (options == null ? void 0 : options.sequential);
|
384
|
-
const test3 = task(
|
385
|
-
|
386
|
-
|
387
|
-
|
441
|
+
const test3 = task(formatName(name2), {
|
442
|
+
...this,
|
443
|
+
...options,
|
444
|
+
handler
|
445
|
+
});
|
388
446
|
test3.type = "test";
|
389
447
|
});
|
390
448
|
const collector = {
|
@@ -403,8 +461,9 @@ function createSuiteCollector(name, factory = () => {
|
|
403
461
|
getHooks(suite2)[name2].push(...fn);
|
404
462
|
}
|
405
463
|
function initSuite(includeLocation) {
|
406
|
-
if (typeof suiteOptions === "number")
|
464
|
+
if (typeof suiteOptions === "number") {
|
407
465
|
suiteOptions = { timeout: suiteOptions };
|
466
|
+
}
|
408
467
|
suite2 = {
|
409
468
|
id: "",
|
410
469
|
type: "suite",
|
@@ -414,7 +473,8 @@ function createSuiteCollector(name, factory = () => {
|
|
414
473
|
file: void 0,
|
415
474
|
shuffle,
|
416
475
|
tasks: [],
|
417
|
-
meta: /* @__PURE__ */ Object.create(null)
|
476
|
+
meta: /* @__PURE__ */ Object.create(null),
|
477
|
+
concurrent: suiteOptions == null ? void 0 : suiteOptions.concurrent
|
418
478
|
};
|
419
479
|
if (runner && includeLocation && runner.config.includeTaskLocation) {
|
420
480
|
const limit = Error.stackTraceLimit;
|
@@ -422,8 +482,9 @@ function createSuiteCollector(name, factory = () => {
|
|
422
482
|
const error = new Error("stacktrace").stack;
|
423
483
|
Error.stackTraceLimit = limit;
|
424
484
|
const stack = findTestFileStackTrace(error, suite2.each ?? false);
|
425
|
-
if (stack)
|
485
|
+
if (stack) {
|
426
486
|
suite2.location = stack;
|
487
|
+
}
|
427
488
|
}
|
428
489
|
setHooks(suite2, createSuiteHooks());
|
429
490
|
}
|
@@ -433,14 +494,17 @@ function createSuiteCollector(name, factory = () => {
|
|
433
494
|
initSuite(false);
|
434
495
|
}
|
435
496
|
async function collect(file) {
|
436
|
-
if (!file)
|
497
|
+
if (!file) {
|
437
498
|
throw new TypeError("File is required to collect tasks.");
|
499
|
+
}
|
438
500
|
factoryQueue.length = 0;
|
439
|
-
if (factory)
|
501
|
+
if (factory) {
|
440
502
|
await runWithSuite(collector, () => factory(test2));
|
503
|
+
}
|
441
504
|
const allChildren = [];
|
442
|
-
for (const i of [...factoryQueue, ...tasks])
|
505
|
+
for (const i of [...factoryQueue, ...tasks]) {
|
443
506
|
allChildren.push(i.type === "collector" ? await i.collect(file) : i);
|
507
|
+
}
|
444
508
|
suite2.file = file;
|
445
509
|
suite2.tasks = allChildren;
|
446
510
|
allChildren.forEach((task2) => {
|
@@ -460,29 +524,41 @@ function createSuite() {
|
|
460
524
|
factoryOrOptions,
|
461
525
|
optionsOrFactory
|
462
526
|
);
|
463
|
-
if (currentSuite == null ? void 0 : currentSuite.options)
|
527
|
+
if (currentSuite == null ? void 0 : currentSuite.options) {
|
464
528
|
options = { ...currentSuite.options, ...options };
|
465
|
-
|
466
|
-
|
467
|
-
|
529
|
+
}
|
530
|
+
const isConcurrent = options.concurrent || this.concurrent && !this.sequential;
|
531
|
+
const isSequential = options.sequential || this.sequential && !this.concurrent;
|
532
|
+
options.concurrent = isConcurrent && !isSequential;
|
533
|
+
options.sequential = isSequential && !isConcurrent;
|
534
|
+
return createSuiteCollector(
|
535
|
+
formatName(name),
|
536
|
+
factory,
|
537
|
+
mode,
|
538
|
+
this.shuffle,
|
539
|
+
this.each,
|
540
|
+
options
|
541
|
+
);
|
468
542
|
}
|
469
543
|
suiteFn.each = function(cases, ...args) {
|
470
544
|
const suite2 = this.withContext();
|
471
545
|
this.setContext("each", true);
|
472
|
-
if (Array.isArray(cases) && args.length)
|
546
|
+
if (Array.isArray(cases) && args.length) {
|
473
547
|
cases = formatTemplateString(cases, args);
|
548
|
+
}
|
474
549
|
return (name, optionsOrFn, fnOrOptions) => {
|
475
550
|
const _name = formatName(name);
|
476
551
|
const arrayOnlyCases = cases.every(Array.isArray);
|
477
|
-
const { options, handler } = parseArguments(
|
478
|
-
optionsOrFn,
|
479
|
-
fnOrOptions
|
480
|
-
);
|
552
|
+
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
481
553
|
const fnFirst = typeof optionsOrFn === "function";
|
482
554
|
cases.forEach((i, idx) => {
|
483
555
|
const items = Array.isArray(i) ? i : [i];
|
484
556
|
if (fnFirst) {
|
485
|
-
arrayOnlyCases ? suite2(
|
557
|
+
arrayOnlyCases ? suite2(
|
558
|
+
formatTitle(_name, items, idx),
|
559
|
+
() => handler(...items),
|
560
|
+
options
|
561
|
+
) : suite2(formatTitle(_name, items, idx), () => handler(i), options);
|
486
562
|
} else {
|
487
563
|
arrayOnlyCases ? suite2(formatTitle(_name, items, idx), options, () => handler(...items)) : suite2(formatTitle(_name, items, idx), options, () => handler(i));
|
488
564
|
}
|
@@ -502,20 +578,22 @@ function createTaskCollector(fn, context) {
|
|
502
578
|
taskFn.each = function(cases, ...args) {
|
503
579
|
const test2 = this.withContext();
|
504
580
|
this.setContext("each", true);
|
505
|
-
if (Array.isArray(cases) && args.length)
|
581
|
+
if (Array.isArray(cases) && args.length) {
|
506
582
|
cases = formatTemplateString(cases, args);
|
583
|
+
}
|
507
584
|
return (name, optionsOrFn, fnOrOptions) => {
|
508
585
|
const _name = formatName(name);
|
509
586
|
const arrayOnlyCases = cases.every(Array.isArray);
|
510
|
-
const { options, handler } = parseArguments(
|
511
|
-
optionsOrFn,
|
512
|
-
fnOrOptions
|
513
|
-
);
|
587
|
+
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
514
588
|
const fnFirst = typeof optionsOrFn === "function";
|
515
589
|
cases.forEach((i, idx) => {
|
516
590
|
const items = Array.isArray(i) ? i : [i];
|
517
591
|
if (fnFirst) {
|
518
|
-
arrayOnlyCases ? test2(
|
592
|
+
arrayOnlyCases ? test2(
|
593
|
+
formatTitle(_name, items, idx),
|
594
|
+
() => handler(...items),
|
595
|
+
options
|
596
|
+
) : test2(formatTitle(_name, items, idx), () => handler(i), options);
|
519
597
|
} else {
|
520
598
|
arrayOnlyCases ? test2(formatTitle(_name, items, idx), options, () => handler(...items)) : test2(formatTitle(_name, items, idx), options, () => handler(i));
|
521
599
|
}
|
@@ -523,6 +601,22 @@ function createTaskCollector(fn, context) {
|
|
523
601
|
this.setContext("each", void 0);
|
524
602
|
};
|
525
603
|
};
|
604
|
+
taskFn.for = function(cases, ...args) {
|
605
|
+
const test2 = this.withContext();
|
606
|
+
if (Array.isArray(cases) && args.length) {
|
607
|
+
cases = formatTemplateString(cases, args);
|
608
|
+
}
|
609
|
+
return (name, optionsOrFn, fnOrOptions) => {
|
610
|
+
const _name = formatName(name);
|
611
|
+
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
612
|
+
cases.forEach((item, idx) => {
|
613
|
+
const handlerWrapper = (ctx) => handler(item, ctx);
|
614
|
+
handlerWrapper.__VITEST_FIXTURE_INDEX__ = 1;
|
615
|
+
handlerWrapper.toString = () => handler.toString();
|
616
|
+
test2(formatTitle(_name, toArray(item), idx), options, handlerWrapper);
|
617
|
+
});
|
618
|
+
};
|
619
|
+
};
|
526
620
|
taskFn.skipIf = function(condition) {
|
527
621
|
return condition ? this.skip : this;
|
528
622
|
};
|
@@ -532,15 +626,21 @@ function createTaskCollector(fn, context) {
|
|
532
626
|
taskFn.extend = function(fixtures) {
|
533
627
|
const _context = mergeContextFixtures(fixtures, context);
|
534
628
|
return createTest(function fn2(name, optionsOrFn, optionsOrTest) {
|
535
|
-
getCurrentSuite().test.fn.call(
|
629
|
+
getCurrentSuite().test.fn.call(
|
630
|
+
this,
|
631
|
+
formatName(name),
|
632
|
+
optionsOrFn,
|
633
|
+
optionsOrTest
|
634
|
+
);
|
536
635
|
}, _context);
|
537
636
|
};
|
538
637
|
const _test = createChainable(
|
539
638
|
["concurrent", "sequential", "skip", "only", "todo", "fails"],
|
540
639
|
taskFn
|
541
640
|
);
|
542
|
-
if (context)
|
641
|
+
if (context) {
|
543
642
|
_test.mergeContext(context);
|
643
|
+
}
|
544
644
|
return _test;
|
545
645
|
}
|
546
646
|
function createTest(fn, context) {
|
@@ -554,14 +654,28 @@ function formatTitle(template, items, idx) {
|
|
554
654
|
template = template.replace(/%%/g, "__vitest_escaped_%__").replace(/%#/g, `${idx}`).replace(/__vitest_escaped_%__/g, "%%");
|
555
655
|
}
|
556
656
|
const count = template.split("%").length - 1;
|
657
|
+
if (template.includes("%f")) {
|
658
|
+
const placeholders = template.match(/%f/g) || [];
|
659
|
+
placeholders.forEach((_, i) => {
|
660
|
+
if (isNegativeNaN(items[i]) || Object.is(items[i], -0)) {
|
661
|
+
let occurrence = 0;
|
662
|
+
template = template.replace(/%f/g, (match) => {
|
663
|
+
occurrence++;
|
664
|
+
return occurrence === i + 1 ? "-%f" : match;
|
665
|
+
});
|
666
|
+
}
|
667
|
+
});
|
668
|
+
}
|
557
669
|
let formatted = format(template, ...items.slice(0, count));
|
558
670
|
if (isObject(items[0])) {
|
559
671
|
formatted = formatted.replace(
|
560
|
-
/\$([$\
|
672
|
+
/\$([$\w.]+)/g,
|
561
673
|
// https://github.com/chaijs/chai/pull/1490
|
562
674
|
(_, key) => {
|
563
675
|
var _a, _b;
|
564
|
-
return objDisplay(objectAttr(items[0], key), {
|
676
|
+
return objDisplay(objectAttr(items[0], key), {
|
677
|
+
truncate: (_b = (_a = runner == null ? void 0 : runner.config) == null ? void 0 : _a.chaiConfig) == null ? void 0 : _b.truncateThreshold
|
678
|
+
});
|
565
679
|
}
|
566
680
|
);
|
567
681
|
}
|
@@ -572,8 +686,9 @@ function formatTemplateString(cases, args) {
|
|
572
686
|
const res = [];
|
573
687
|
for (let i = 0; i < Math.floor(args.length / header.length); i++) {
|
574
688
|
const oneCase = {};
|
575
|
-
for (let j = 0; j < header.length; j++)
|
689
|
+
for (let j = 0; j < header.length; j++) {
|
576
690
|
oneCase[header[j]] = args[i * header.length + j];
|
691
|
+
}
|
577
692
|
res.push(oneCase);
|
578
693
|
}
|
579
694
|
return res;
|
@@ -606,29 +721,20 @@ async function runSetupFiles(config, runner) {
|
|
606
721
|
})
|
607
722
|
);
|
608
723
|
} else {
|
609
|
-
for (const fsPath of files)
|
724
|
+
for (const fsPath of files) {
|
610
725
|
await runner.importFile(fsPath, "setup");
|
726
|
+
}
|
611
727
|
}
|
612
728
|
}
|
613
729
|
|
614
730
|
const now$1 = Date.now;
|
615
731
|
async function collectTests(paths, runner) {
|
732
|
+
var _a;
|
616
733
|
const files = [];
|
617
734
|
const config = runner.config;
|
618
735
|
for (const filepath of paths) {
|
619
|
-
const
|
620
|
-
|
621
|
-
id: generateHash(`${path}${config.name || ""}`),
|
622
|
-
name: path,
|
623
|
-
type: "suite",
|
624
|
-
mode: "run",
|
625
|
-
filepath,
|
626
|
-
tasks: [],
|
627
|
-
meta: /* @__PURE__ */ Object.create(null),
|
628
|
-
projectName: config.name,
|
629
|
-
file: void 0
|
630
|
-
};
|
631
|
-
file.file = file;
|
736
|
+
const file = createFileTask(filepath, config.root, config.name);
|
737
|
+
(_a = runner.onCollectStart) == null ? void 0 : _a.call(runner, file);
|
632
738
|
clearCollectorContext(filepath, runner);
|
633
739
|
try {
|
634
740
|
const setupStart = now$1();
|
@@ -663,11 +769,18 @@ async function collectTests(paths, runner) {
|
|
663
769
|
}
|
664
770
|
calculateSuiteHash(file);
|
665
771
|
const hasOnlyTasks = someTasksAreOnly(file);
|
666
|
-
interpretTaskModes(
|
772
|
+
interpretTaskModes(
|
773
|
+
file,
|
774
|
+
config.testNamePattern,
|
775
|
+
hasOnlyTasks,
|
776
|
+
false,
|
777
|
+
config.allowOnly
|
778
|
+
);
|
667
779
|
file.tasks.forEach((task) => {
|
668
|
-
var
|
669
|
-
if (((
|
780
|
+
var _a2;
|
781
|
+
if (((_a2 = task.suite) == null ? void 0 : _a2.id) === "") {
|
670
782
|
delete task.suite;
|
783
|
+
}
|
671
784
|
});
|
672
785
|
files.push(file);
|
673
786
|
}
|
@@ -684,10 +797,12 @@ function mergeHooks(baseHooks, hooks) {
|
|
684
797
|
const now = Date.now;
|
685
798
|
function updateSuiteHookState(suite, name, state, runner) {
|
686
799
|
var _a;
|
687
|
-
if (!suite.result)
|
800
|
+
if (!suite.result) {
|
688
801
|
suite.result = { state: "run" };
|
689
|
-
|
802
|
+
}
|
803
|
+
if (!((_a = suite.result) == null ? void 0 : _a.hooks)) {
|
690
804
|
suite.result.hooks = {};
|
805
|
+
}
|
691
806
|
const suiteHooks = suite.result.hooks;
|
692
807
|
if (suiteHooks) {
|
693
808
|
suiteHooks[name] = state;
|
@@ -696,18 +811,21 @@ function updateSuiteHookState(suite, name, state, runner) {
|
|
696
811
|
}
|
697
812
|
function getSuiteHooks(suite, name, sequence) {
|
698
813
|
const hooks = getHooks(suite)[name];
|
699
|
-
if (sequence === "stack" && (name === "afterAll" || name === "afterEach"))
|
814
|
+
if (sequence === "stack" && (name === "afterAll" || name === "afterEach")) {
|
700
815
|
return hooks.slice().reverse();
|
816
|
+
}
|
701
817
|
return hooks;
|
702
818
|
}
|
703
819
|
async function callTaskHooks(task, hooks, sequence) {
|
704
|
-
if (sequence === "stack")
|
820
|
+
if (sequence === "stack") {
|
705
821
|
hooks = hooks.slice().reverse();
|
822
|
+
}
|
706
823
|
if (sequence === "parallel") {
|
707
824
|
await Promise.all(hooks.map((fn) => fn(task.result)));
|
708
825
|
} else {
|
709
|
-
for (const fn of hooks)
|
826
|
+
for (const fn of hooks) {
|
710
827
|
await fn(task.result);
|
828
|
+
}
|
711
829
|
}
|
712
830
|
}
|
713
831
|
async function callSuiteHook(suite, currentTask, name, runner, args) {
|
@@ -722,10 +840,13 @@ async function callSuiteHook(suite, currentTask, name, runner, args) {
|
|
722
840
|
updateSuiteHookState(currentTask, name, "run", runner);
|
723
841
|
const hooks = getSuiteHooks(suite, name, sequence);
|
724
842
|
if (sequence === "parallel") {
|
725
|
-
callbacks.push(
|
843
|
+
callbacks.push(
|
844
|
+
...await Promise.all(hooks.map((fn) => fn(...args)))
|
845
|
+
);
|
726
846
|
} else {
|
727
|
-
for (const hook of hooks)
|
847
|
+
for (const hook of hooks) {
|
728
848
|
callbacks.push(await hook(...args));
|
849
|
+
}
|
729
850
|
}
|
730
851
|
updateSuiteHookState(currentTask, name, "pass", runner);
|
731
852
|
if (name === "afterEach" && parentSuite) {
|
@@ -753,11 +874,7 @@ async function sendTasksUpdate(runner) {
|
|
753
874
|
await previousUpdate;
|
754
875
|
if (packs.size) {
|
755
876
|
const taskPacks = Array.from(packs).map(([id, task]) => {
|
756
|
-
return [
|
757
|
-
id,
|
758
|
-
task[0],
|
759
|
-
task[1]
|
760
|
-
];
|
877
|
+
return [id, task[0], task[1]];
|
761
878
|
});
|
762
879
|
const p = (_a = runner.onTaskUpdate) == null ? void 0 : _a.call(runner, taskPacks);
|
763
880
|
packs.clear();
|
@@ -765,17 +882,21 @@ async function sendTasksUpdate(runner) {
|
|
765
882
|
}
|
766
883
|
}
|
767
884
|
async function callCleanupHooks(cleanups) {
|
768
|
-
await Promise.all(
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
885
|
+
await Promise.all(
|
886
|
+
cleanups.map(async (fn) => {
|
887
|
+
if (typeof fn !== "function") {
|
888
|
+
return;
|
889
|
+
}
|
890
|
+
await fn();
|
891
|
+
})
|
892
|
+
);
|
773
893
|
}
|
774
894
|
async function runTest(test, runner) {
|
775
895
|
var _a, _b, _c, _d, _e, _f;
|
776
896
|
await ((_a = runner.onBeforeRunTask) == null ? void 0 : _a.call(runner, test));
|
777
|
-
if (test.mode !== "run")
|
897
|
+
if (test.mode !== "run") {
|
778
898
|
return;
|
899
|
+
}
|
779
900
|
if (((_b = test.result) == null ? void 0 : _b.state) === "fail") {
|
780
901
|
updateTask(test, runner);
|
781
902
|
return;
|
@@ -795,29 +916,46 @@ async function runTest(test, runner) {
|
|
795
916
|
for (let retryCount = 0; retryCount <= retry; retryCount++) {
|
796
917
|
let beforeEachCleanups = [];
|
797
918
|
try {
|
798
|
-
await ((_c = runner.onBeforeTryTask) == null ? void 0 : _c.call(runner, test, {
|
919
|
+
await ((_c = runner.onBeforeTryTask) == null ? void 0 : _c.call(runner, test, {
|
920
|
+
retry: retryCount,
|
921
|
+
repeats: repeatCount
|
922
|
+
}));
|
799
923
|
test.result.repeatCount = repeatCount;
|
800
|
-
beforeEachCleanups = await callSuiteHook(
|
924
|
+
beforeEachCleanups = await callSuiteHook(
|
925
|
+
suite,
|
926
|
+
test,
|
927
|
+
"beforeEach",
|
928
|
+
runner,
|
929
|
+
[test.context, suite]
|
930
|
+
);
|
801
931
|
if (runner.runTask) {
|
802
932
|
await runner.runTask(test);
|
803
933
|
} else {
|
804
934
|
const fn = getFn(test);
|
805
|
-
if (!fn)
|
806
|
-
throw new Error(
|
935
|
+
if (!fn) {
|
936
|
+
throw new Error(
|
937
|
+
"Test function is not found. Did you add it using `setFn`?"
|
938
|
+
);
|
939
|
+
}
|
807
940
|
await fn();
|
808
941
|
}
|
809
942
|
if (test.promises) {
|
810
943
|
const result = await Promise.allSettled(test.promises);
|
811
944
|
const errors = result.map((r) => r.status === "rejected" ? r.reason : void 0).filter(Boolean);
|
812
|
-
if (errors.length)
|
945
|
+
if (errors.length) {
|
813
946
|
throw errors;
|
947
|
+
}
|
814
948
|
}
|
815
|
-
await ((_d = runner.onAfterTryTask) == null ? void 0 : _d.call(runner, test, {
|
949
|
+
await ((_d = runner.onAfterTryTask) == null ? void 0 : _d.call(runner, test, {
|
950
|
+
retry: retryCount,
|
951
|
+
repeats: repeatCount
|
952
|
+
}));
|
816
953
|
if (test.result.state !== "fail") {
|
817
|
-
if (!test.repeats)
|
954
|
+
if (!test.repeats) {
|
818
955
|
test.result.state = "pass";
|
819
|
-
else if (test.repeats && retry === retryCount)
|
956
|
+
} else if (test.repeats && retry === retryCount) {
|
820
957
|
test.result.state = "pass";
|
958
|
+
}
|
821
959
|
}
|
822
960
|
} catch (e) {
|
823
961
|
failTask(test.result, e, runner.config.diffOptions);
|
@@ -830,14 +968,18 @@ async function runTest(test, runner) {
|
|
830
968
|
return;
|
831
969
|
}
|
832
970
|
try {
|
833
|
-
await callSuiteHook(suite, test, "afterEach", runner, [
|
971
|
+
await callSuiteHook(suite, test, "afterEach", runner, [
|
972
|
+
test.context,
|
973
|
+
suite
|
974
|
+
]);
|
834
975
|
await callCleanupHooks(beforeEachCleanups);
|
835
976
|
await callFixtureCleanup(test.context);
|
836
977
|
} catch (e) {
|
837
978
|
failTask(test.result, e, runner.config.diffOptions);
|
838
979
|
}
|
839
|
-
if (test.result.state === "pass")
|
980
|
+
if (test.result.state === "pass") {
|
840
981
|
break;
|
982
|
+
}
|
841
983
|
if (retryCount < retry) {
|
842
984
|
test.result.state = "run";
|
843
985
|
test.result.retryCount = (test.result.retryCount ?? 0) + 1;
|
@@ -852,7 +994,11 @@ async function runTest(test, runner) {
|
|
852
994
|
}
|
853
995
|
if (test.result.state === "fail") {
|
854
996
|
try {
|
855
|
-
await callTaskHooks(
|
997
|
+
await callTaskHooks(
|
998
|
+
test,
|
999
|
+
test.onFailed || [],
|
1000
|
+
runner.config.sequence.hooks
|
1001
|
+
);
|
856
1002
|
} catch (e) {
|
857
1003
|
failTask(test.result, e, runner.config.diffOptions);
|
858
1004
|
}
|
@@ -890,8 +1036,9 @@ function markTasksAsSkipped(suite, runner) {
|
|
890
1036
|
t.mode = "skip";
|
891
1037
|
t.result = { ...t.result, state: "skip" };
|
892
1038
|
updateTask(t, runner);
|
893
|
-
if (t.type === "suite")
|
1039
|
+
if (t.type === "suite") {
|
894
1040
|
markTasksAsSkipped(t, runner);
|
1041
|
+
}
|
895
1042
|
});
|
896
1043
|
}
|
897
1044
|
async function runSuite(suite, runner) {
|
@@ -915,24 +1062,34 @@ async function runSuite(suite, runner) {
|
|
915
1062
|
suite.result.state = "todo";
|
916
1063
|
} else {
|
917
1064
|
try {
|
918
|
-
beforeAllCleanups = await callSuiteHook(
|
1065
|
+
beforeAllCleanups = await callSuiteHook(
|
1066
|
+
suite,
|
1067
|
+
suite,
|
1068
|
+
"beforeAll",
|
1069
|
+
runner,
|
1070
|
+
[suite]
|
1071
|
+
);
|
919
1072
|
if (runner.runSuite) {
|
920
1073
|
await runner.runSuite(suite);
|
921
1074
|
} else {
|
922
1075
|
for (let tasksGroup of partitionSuiteChildren(suite)) {
|
923
1076
|
if (tasksGroup[0].concurrent === true) {
|
924
|
-
|
925
|
-
await Promise.all(tasksGroup.map((c) => mutex(() => runSuiteChild(c, runner))));
|
1077
|
+
await Promise.all(tasksGroup.map((c) => runSuiteChild(c, runner)));
|
926
1078
|
} else {
|
927
1079
|
const { sequence } = runner.config;
|
928
1080
|
if (sequence.shuffle || suite.shuffle) {
|
929
|
-
const suites = tasksGroup.filter(
|
1081
|
+
const suites = tasksGroup.filter(
|
1082
|
+
(group) => group.type === "suite"
|
1083
|
+
);
|
930
1084
|
const tests = tasksGroup.filter((group) => group.type === "test");
|
931
1085
|
const groups = shuffle([suites, tests], sequence.seed);
|
932
|
-
tasksGroup = groups.flatMap(
|
1086
|
+
tasksGroup = groups.flatMap(
|
1087
|
+
(group) => shuffle(group, sequence.seed)
|
1088
|
+
);
|
933
1089
|
}
|
934
|
-
for (const c of tasksGroup)
|
1090
|
+
for (const c of tasksGroup) {
|
935
1091
|
await runSuiteChild(c, runner);
|
1092
|
+
}
|
936
1093
|
}
|
937
1094
|
}
|
938
1095
|
}
|
@@ -949,7 +1106,9 @@ async function runSuite(suite, runner) {
|
|
949
1106
|
if (!runner.config.passWithNoTests && !hasTests(suite)) {
|
950
1107
|
suite.result.state = "fail";
|
951
1108
|
if (!((_c = suite.result.errors) == null ? void 0 : _c.length)) {
|
952
|
-
const error = processError(
|
1109
|
+
const error = processError(
|
1110
|
+
new Error(`No test found in suite ${suite.name}`)
|
1111
|
+
);
|
953
1112
|
suite.result.errors = [error];
|
954
1113
|
}
|
955
1114
|
} else if (hasFailed(suite)) {
|
@@ -963,18 +1122,23 @@ async function runSuite(suite, runner) {
|
|
963
1122
|
await ((_d = runner.onAfterRunSuite) == null ? void 0 : _d.call(runner, suite));
|
964
1123
|
}
|
965
1124
|
}
|
1125
|
+
let limitMaxConcurrency;
|
966
1126
|
async function runSuiteChild(c, runner) {
|
967
|
-
if (c.type === "test" || c.type === "custom")
|
968
|
-
return runTest(c, runner);
|
969
|
-
else if (c.type === "suite")
|
1127
|
+
if (c.type === "test" || c.type === "custom") {
|
1128
|
+
return limitMaxConcurrency(() => runTest(c, runner));
|
1129
|
+
} else if (c.type === "suite") {
|
970
1130
|
return runSuite(c, runner);
|
1131
|
+
}
|
971
1132
|
}
|
972
1133
|
async function runFiles(files, runner) {
|
973
1134
|
var _a, _b;
|
1135
|
+
limitMaxConcurrency ?? (limitMaxConcurrency = limit(runner.config.maxConcurrency));
|
974
1136
|
for (const file of files) {
|
975
1137
|
if (!file.tasks.length && !runner.config.passWithNoTests) {
|
976
1138
|
if (!((_b = (_a = file.result) == null ? void 0 : _a.errors) == null ? void 0 : _b.length)) {
|
977
|
-
const error = processError(
|
1139
|
+
const error = processError(
|
1140
|
+
new Error(`No test suite found in file ${file.filepath}`)
|
1141
|
+
);
|
978
1142
|
file.result = {
|
979
1143
|
state: "fail",
|
980
1144
|
errors: [error]
|
@@ -1000,30 +1164,49 @@ function getDefaultHookTimeout() {
|
|
1000
1164
|
return getRunner().config.hookTimeout;
|
1001
1165
|
}
|
1002
1166
|
function beforeAll(fn, timeout) {
|
1003
|
-
return getCurrentSuite().on(
|
1167
|
+
return getCurrentSuite().on(
|
1168
|
+
"beforeAll",
|
1169
|
+
withTimeout(fn, timeout ?? getDefaultHookTimeout(), true)
|
1170
|
+
);
|
1004
1171
|
}
|
1005
1172
|
function afterAll(fn, timeout) {
|
1006
|
-
return getCurrentSuite().on(
|
1173
|
+
return getCurrentSuite().on(
|
1174
|
+
"afterAll",
|
1175
|
+
withTimeout(fn, timeout ?? getDefaultHookTimeout(), true)
|
1176
|
+
);
|
1007
1177
|
}
|
1008
1178
|
function beforeEach(fn, timeout) {
|
1009
|
-
return getCurrentSuite().on(
|
1179
|
+
return getCurrentSuite().on(
|
1180
|
+
"beforeEach",
|
1181
|
+
withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true)
|
1182
|
+
);
|
1010
1183
|
}
|
1011
1184
|
function afterEach(fn, timeout) {
|
1012
|
-
return getCurrentSuite().on(
|
1185
|
+
return getCurrentSuite().on(
|
1186
|
+
"afterEach",
|
1187
|
+
withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true)
|
1188
|
+
);
|
1013
1189
|
}
|
1014
|
-
const onTestFailed = createTestHook(
|
1015
|
-
|
1016
|
-
test
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1190
|
+
const onTestFailed = createTestHook(
|
1191
|
+
"onTestFailed",
|
1192
|
+
(test, handler) => {
|
1193
|
+
test.onFailed || (test.onFailed = []);
|
1194
|
+
test.onFailed.push(handler);
|
1195
|
+
}
|
1196
|
+
);
|
1197
|
+
const onTestFinished = createTestHook(
|
1198
|
+
"onTestFinished",
|
1199
|
+
(test, handler) => {
|
1200
|
+
test.onFinished || (test.onFinished = []);
|
1201
|
+
test.onFinished.push(handler);
|
1202
|
+
}
|
1203
|
+
);
|
1022
1204
|
function createTestHook(name, handler) {
|
1023
1205
|
return (fn) => {
|
1024
1206
|
const current = getCurrentTest();
|
1025
|
-
if (!current)
|
1207
|
+
if (!current) {
|
1026
1208
|
throw new Error(`Hook ${name}() can only be called inside a test`);
|
1209
|
+
}
|
1027
1210
|
return handler(current, fn);
|
1028
1211
|
};
|
1029
1212
|
}
|
@@ -64,7 +64,11 @@ interface TaskResult {
|
|
64
64
|
retryCount?: number;
|
65
65
|
repeatCount?: number;
|
66
66
|
}
|
67
|
-
type TaskResultPack = [
|
67
|
+
type TaskResultPack = [
|
68
|
+
id: string,
|
69
|
+
result: TaskResult | undefined,
|
70
|
+
meta: TaskMeta
|
71
|
+
];
|
68
72
|
interface Suite extends TaskBase {
|
69
73
|
file: File;
|
70
74
|
type: 'suite';
|
@@ -72,7 +76,7 @@ interface Suite extends TaskBase {
|
|
72
76
|
}
|
73
77
|
interface File extends Suite {
|
74
78
|
filepath: string;
|
75
|
-
projectName: string;
|
79
|
+
projectName: string | undefined;
|
76
80
|
collectDuration?: number;
|
77
81
|
setupDuration?: number;
|
78
82
|
}
|
@@ -114,6 +118,14 @@ interface TestEachFunction {
|
|
114
118
|
<T>(cases: ReadonlyArray<T>): EachFunctionReturn<T[]>;
|
115
119
|
(...args: [TemplateStringsArray, ...any]): EachFunctionReturn<any[]>;
|
116
120
|
}
|
121
|
+
interface TestForFunctionReturn<Arg, Context> {
|
122
|
+
(name: string | Function, fn: (arg: Arg, context: Context) => Awaitable<void>): void;
|
123
|
+
(name: string | Function, options: TestOptions, fn: (args: Arg, context: Context) => Awaitable<void>): void;
|
124
|
+
}
|
125
|
+
interface TestForFunction<ExtraContext> {
|
126
|
+
<T>(cases: ReadonlyArray<T>): TestForFunctionReturn<T, ExtendedContext<Test> & ExtraContext>;
|
127
|
+
(strings: TemplateStringsArray, ...values: any[]): TestForFunctionReturn<any, ExtendedContext<Test> & ExtraContext>;
|
128
|
+
}
|
117
129
|
interface TestCollectorCallable<C = {}> {
|
118
130
|
/**
|
119
131
|
* @deprecated Use options as the second argument instead
|
@@ -124,6 +136,7 @@ interface TestCollectorCallable<C = {}> {
|
|
124
136
|
}
|
125
137
|
type ChainableTestAPI<ExtraContext = {}> = ChainableFunction<'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'fails', TestCollectorCallable<ExtraContext>, {
|
126
138
|
each: TestEachFunction;
|
139
|
+
for: TestForFunction<ExtraContext>;
|
127
140
|
}>;
|
128
141
|
interface TestOptions {
|
129
142
|
/**
|
@@ -145,7 +158,7 @@ interface TestOptions {
|
|
145
158
|
*/
|
146
159
|
repeats?: number;
|
147
160
|
/**
|
148
|
-
* Whether tests run concurrently.
|
161
|
+
* Whether suites and tests run concurrently.
|
149
162
|
* Tests inherit `concurrent` from `describe()` and nested `describe()` will inherit from parent's `concurrent`.
|
150
163
|
*/
|
151
164
|
concurrent?: boolean;
|
@@ -193,7 +206,7 @@ interface FixtureOptions {
|
|
193
206
|
}
|
194
207
|
type Use<T> = (value: T) => Promise<void>;
|
195
208
|
type FixtureFn<T, K extends keyof T, ExtraContext> = (context: Omit<T, K> & ExtraContext, use: Use<T[K]>) => Promise<void>;
|
196
|
-
type Fixture<T, K extends keyof T, ExtraContext = {}> = ((...args: any) => any) extends T[K] ?
|
209
|
+
type Fixture<T, K extends keyof T, ExtraContext = {}> = ((...args: any) => any) extends T[K] ? T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never : T[K] | (T[K] extends any ? FixtureFn<T, K, Omit<ExtraContext, Exclude<keyof T, K>>> : never);
|
197
210
|
type Fixtures<T extends Record<string, any>, ExtraContext = {}> = {
|
198
211
|
[K in keyof T]: Fixture<T, K, ExtraContext & ExtendedContext<Test>> | [Fixture<T, K, ExtraContext & ExtendedContext<Test>>, FixtureOptions?];
|
199
212
|
};
|
@@ -218,8 +231,14 @@ type HookCleanupCallback = (() => Awaitable<unknown>) | void;
|
|
218
231
|
interface SuiteHooks<ExtraContext = {}> {
|
219
232
|
beforeAll: HookListener<[Readonly<Suite | File>], HookCleanupCallback>[];
|
220
233
|
afterAll: HookListener<[Readonly<Suite | File>]>[];
|
221
|
-
beforeEach: HookListener<[
|
222
|
-
|
234
|
+
beforeEach: HookListener<[
|
235
|
+
ExtendedContext<Test | Custom> & ExtraContext,
|
236
|
+
Readonly<Suite>
|
237
|
+
], HookCleanupCallback>[];
|
238
|
+
afterEach: HookListener<[
|
239
|
+
ExtendedContext<Test | Custom> & ExtraContext,
|
240
|
+
Readonly<Suite>
|
241
|
+
]>[];
|
223
242
|
}
|
224
243
|
interface TaskCustomOptions extends TestOptions {
|
225
244
|
concurrent?: boolean;
|
package/dist/types.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { B as SequenceHooks, G as SequenceSetupFiles, F as File, T as Task, S as Suite, o as TaskResultPack, a as Test, C as Custom, A as TaskContext, E as ExtendedContext } from './tasks-
|
2
|
-
export { g as CustomAPI, D as DoneCallback, t as Fixture, s as FixtureFn, r as FixtureOptions, u as Fixtures, v as HookCleanupCallback, H as HookListener, I as InferFixturesTypes, O as OnTestFailedHandler, i as OnTestFinishedHandler, R as RunMode, y as RuntimeContext, d as SuiteAPI, f as SuiteCollector, x as SuiteFactory, h as SuiteHooks, k as TaskBase, w as TaskCustomOptions, m as TaskMeta, l as TaskPopulated, n as TaskResult, j as TaskState, e as TestAPI, z as TestContext, p as TestFunction, q as TestOptions, U as Use } from './tasks-
|
1
|
+
import { B as SequenceHooks, G as SequenceSetupFiles, F as File, T as Task, S as Suite, o as TaskResultPack, a as Test, C as Custom, A as TaskContext, E as ExtendedContext } from './tasks-BP89OzIP.js';
|
2
|
+
export { g as CustomAPI, D as DoneCallback, t as Fixture, s as FixtureFn, r as FixtureOptions, u as Fixtures, v as HookCleanupCallback, H as HookListener, I as InferFixturesTypes, O as OnTestFailedHandler, i as OnTestFinishedHandler, R as RunMode, y as RuntimeContext, d as SuiteAPI, f as SuiteCollector, x as SuiteFactory, h as SuiteHooks, k as TaskBase, w as TaskCustomOptions, m as TaskMeta, l as TaskPopulated, n as TaskResult, j as TaskState, e as TestAPI, z as TestContext, p as TestFunction, q as TestOptions, U as Use } from './tasks-BP89OzIP.js';
|
3
3
|
import { DiffOptions } from '@vitest/utils/diff';
|
4
4
|
import '@vitest/utils';
|
5
5
|
|
@@ -31,12 +31,16 @@ type VitestRunnerImportSource = 'collect' | 'setup';
|
|
31
31
|
interface VitestRunnerConstructor {
|
32
32
|
new (config: VitestRunnerConfig): VitestRunner;
|
33
33
|
}
|
34
|
-
type CancelReason = 'keyboard-input' | 'test-failure' | string & Record<string, never
|
34
|
+
type CancelReason = 'keyboard-input' | 'test-failure' | (string & Record<string, never>);
|
35
35
|
interface VitestRunner {
|
36
36
|
/**
|
37
37
|
* First thing that's getting called before actually collecting and running tests.
|
38
38
|
*/
|
39
39
|
onBeforeCollect?: (paths: string[]) => unknown;
|
40
|
+
/**
|
41
|
+
* Called after the file task was created but not collected yet.
|
42
|
+
*/
|
43
|
+
onCollectStart?: (file: File) => unknown;
|
40
44
|
/**
|
41
45
|
* Called after collecting tests and before "onBeforeRun".
|
42
46
|
*/
|
package/dist/utils.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { S as Suite, T as Task, a as Test, C as Custom } from './tasks-
|
2
|
-
export { b as ChainableFunction, c as createChainable } from './tasks-
|
1
|
+
import { S as Suite, F as File, T as Task, a as Test, C as Custom } from './tasks-BP89OzIP.js';
|
2
|
+
export { b as ChainableFunction, c as createChainable } from './tasks-BP89OzIP.js';
|
3
3
|
import { Arrayable } from '@vitest/utils';
|
4
4
|
|
5
5
|
/**
|
@@ -9,12 +9,14 @@ declare function interpretTaskModes(suite: Suite, namePattern?: string | RegExp,
|
|
9
9
|
declare function someTasksAreOnly(suite: Suite): boolean;
|
10
10
|
declare function generateHash(str: string): string;
|
11
11
|
declare function calculateSuiteHash(parent: Suite): void;
|
12
|
+
declare function createFileTask(filepath: string, root: string, projectName: string): File;
|
12
13
|
|
13
14
|
/**
|
14
15
|
* Partition in tasks groups by consecutive concurrent
|
15
16
|
*/
|
16
17
|
declare function partitionSuiteChildren(suite: Suite): Task[][];
|
17
18
|
|
19
|
+
declare function isAtomTest(s: Task): s is Test | Custom;
|
18
20
|
declare function getTests(suite: Arrayable<Task>): (Test | Custom)[];
|
19
21
|
declare function getTasks(tasks?: Arrayable<Task>): Task[];
|
20
22
|
declare function getSuites(suite: Arrayable<Task>): Suite[];
|
@@ -22,4 +24,4 @@ declare function hasTests(suite: Arrayable<Suite>): boolean;
|
|
22
24
|
declare function hasFailed(suite: Arrayable<Task>): boolean;
|
23
25
|
declare function getNames(task: Task): string[];
|
24
26
|
|
25
|
-
export { calculateSuiteHash, generateHash, getNames, getSuites, getTasks, getTests, hasFailed, hasTests, interpretTaskModes, partitionSuiteChildren, someTasksAreOnly };
|
27
|
+
export { calculateSuiteHash, createFileTask, generateHash, getNames, getSuites, getTasks, getTests, hasFailed, hasTests, interpretTaskModes, isAtomTest, partitionSuiteChildren, someTasksAreOnly };
|
package/dist/utils.js
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
-
export { c as calculateSuiteHash,
|
1
|
+
export { c as calculateSuiteHash, l as createChainable, a as createFileTask, g as generateHash, k as getNames, f as getSuites, e as getTasks, d as getTests, j as hasFailed, h as hasTests, i as interpretTaskModes, b as isAtomTest, p as partitionSuiteChildren, s as someTasksAreOnly } from './chunk-tasks.js';
|
2
2
|
import '@vitest/utils/error';
|
3
|
+
import 'pathe';
|
3
4
|
import '@vitest/utils';
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitest/runner",
|
3
3
|
"type": "module",
|
4
|
-
"version": "2.0.0-beta.
|
4
|
+
"version": "2.0.0-beta.11",
|
5
5
|
"description": "Vitest test runner",
|
6
6
|
"license": "MIT",
|
7
7
|
"funding": "https://opencollective.com/vitest",
|
@@ -40,7 +40,7 @@
|
|
40
40
|
"dependencies": {
|
41
41
|
"p-limit": "^5.0.0",
|
42
42
|
"pathe": "^1.1.2",
|
43
|
-
"@vitest/utils": "2.0.0-beta.
|
43
|
+
"@vitest/utils": "2.0.0-beta.11"
|
44
44
|
},
|
45
45
|
"scripts": {
|
46
46
|
"build": "rimraf dist && rollup -c",
|