@vitest/runner 2.0.0-beta.9 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,11 +13,39 @@ function partitionSuiteChildren(suite) {
13
13
  tasksGroup = [c];
14
14
  }
15
15
  }
16
- if (tasksGroup.length > 0)
16
+ if (tasksGroup.length > 0) {
17
17
  tasksGroups.push(tasksGroup);
18
+ }
18
19
  return tasksGroups;
19
20
  }
20
21
 
22
+ function limitConcurrency(concurrency = Infinity) {
23
+ let count = 0;
24
+ let head;
25
+ let tail;
26
+ const finish = () => {
27
+ count--;
28
+ if (head) {
29
+ head[0]();
30
+ head = head[1];
31
+ tail = head && tail;
32
+ }
33
+ };
34
+ return (func, ...args) => {
35
+ return new Promise((resolve) => {
36
+ if (count++ < concurrency) {
37
+ resolve();
38
+ } else if (tail) {
39
+ tail = tail[1] = [resolve];
40
+ } else {
41
+ head = tail = [resolve];
42
+ }
43
+ }).then(() => {
44
+ return func(...args);
45
+ }).finally(finish);
46
+ };
47
+ }
48
+
21
49
  function interpretTaskModes(suite, namePattern, onlyMode, parentIsOnly, allowOnly) {
22
50
  const suiteIsOnly = parentIsOnly || suite.mode === "only";
23
51
  suite.tasks.forEach((t) => {
@@ -36,39 +64,50 @@ function interpretTaskModes(suite, namePattern, onlyMode, parentIsOnly, allowOnl
36
64
  }
37
65
  }
38
66
  if (t.type === "test") {
39
- if (namePattern && !getTaskFullName(t).match(namePattern))
67
+ if (namePattern && !getTaskFullName(t).match(namePattern)) {
40
68
  t.mode = "skip";
69
+ }
41
70
  } else if (t.type === "suite") {
42
- if (t.mode === "skip")
71
+ if (t.mode === "skip") {
43
72
  skipAllTasks(t);
44
- else
73
+ } else {
45
74
  interpretTaskModes(t, namePattern, onlyMode, includeTask, allowOnly);
75
+ }
46
76
  }
47
77
  });
48
78
  if (suite.mode === "run") {
49
- if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run"))
79
+ if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run")) {
50
80
  suite.mode = "skip";
81
+ }
51
82
  }
52
83
  }
53
84
  function getTaskFullName(task) {
54
85
  return `${task.suite ? `${getTaskFullName(task.suite)} ` : ""}${task.name}`;
55
86
  }
56
87
  function someTasksAreOnly(suite) {
57
- return suite.tasks.some((t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t));
88
+ return suite.tasks.some(
89
+ (t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t)
90
+ );
58
91
  }
59
92
  function skipAllTasks(suite) {
60
93
  suite.tasks.forEach((t) => {
61
94
  if (t.mode === "run") {
62
95
  t.mode = "skip";
63
- if (t.type === "suite")
96
+ if (t.type === "suite") {
64
97
  skipAllTasks(t);
98
+ }
65
99
  }
66
100
  });
67
101
  }
68
102
  function checkAllowOnly(task, allowOnly) {
69
- if (allowOnly)
103
+ if (allowOnly) {
70
104
  return;
71
- const error = processError(new Error("[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error"));
105
+ }
106
+ const error = processError(
107
+ new Error(
108
+ "[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error"
109
+ )
110
+ );
72
111
  task.result = {
73
112
  state: "fail",
74
113
  errors: [error]
@@ -76,8 +115,9 @@ function checkAllowOnly(task, allowOnly) {
76
115
  }
77
116
  function generateHash(str) {
78
117
  let hash = 0;
79
- if (str.length === 0)
118
+ if (str.length === 0) {
80
119
  return `${hash}`;
120
+ }
81
121
  for (let i = 0; i < str.length; i++) {
82
122
  const char = str.charCodeAt(i);
83
123
  hash = (hash << 5) - hash + char;
@@ -88,11 +128,12 @@ function generateHash(str) {
88
128
  function calculateSuiteHash(parent) {
89
129
  parent.tasks.forEach((t, idx) => {
90
130
  t.id = `${parent.id}_${idx}`;
91
- if (t.type === "suite")
131
+ if (t.type === "suite") {
92
132
  calculateSuiteHash(t);
133
+ }
93
134
  });
94
135
  }
95
- function createFileTask(filepath, root, projectName) {
136
+ function createFileTask(filepath, root, projectName, pool) {
96
137
  const path = relative(root, filepath);
97
138
  const file = {
98
139
  id: generateHash(`${path}${projectName || ""}`),
@@ -103,7 +144,8 @@ function createFileTask(filepath, root, projectName) {
103
144
  tasks: [],
104
145
  meta: /* @__PURE__ */ Object.create(null),
105
146
  projectName,
106
- file: void 0
147
+ file: void 0,
148
+ pool
107
149
  };
108
150
  file.file = file;
109
151
  return file;
@@ -151,8 +193,9 @@ function getTests(suite) {
151
193
  tests.push(task);
152
194
  } else {
153
195
  const taskTests = getTests(task);
154
- for (const test of taskTests)
196
+ for (const test of taskTests) {
155
197
  tests.push(test);
198
+ }
156
199
  }
157
200
  }
158
201
  }
@@ -160,31 +203,41 @@ function getTests(suite) {
160
203
  return tests;
161
204
  }
162
205
  function getTasks(tasks = []) {
163
- return toArray(tasks).flatMap((s) => isAtomTest(s) ? [s] : [s, ...getTasks(s.tasks)]);
206
+ return toArray(tasks).flatMap(
207
+ (s) => isAtomTest(s) ? [s] : [s, ...getTasks(s.tasks)]
208
+ );
164
209
  }
165
210
  function getSuites(suite) {
166
- return toArray(suite).flatMap((s) => s.type === "suite" ? [s, ...getSuites(s.tasks)] : []);
211
+ return toArray(suite).flatMap(
212
+ (s) => s.type === "suite" ? [s, ...getSuites(s.tasks)] : []
213
+ );
167
214
  }
168
215
  function hasTests(suite) {
169
- return toArray(suite).some((s) => s.tasks.some((c) => isAtomTest(c) || hasTests(c)));
216
+ return toArray(suite).some(
217
+ (s) => s.tasks.some((c) => isAtomTest(c) || hasTests(c))
218
+ );
170
219
  }
171
220
  function hasFailed(suite) {
172
- return toArray(suite).some((s) => {
173
- var _a;
174
- return ((_a = s.result) == null ? void 0 : _a.state) === "fail" || s.type === "suite" && hasFailed(s.tasks);
175
- });
221
+ return toArray(suite).some(
222
+ (s) => {
223
+ var _a;
224
+ return ((_a = s.result) == null ? void 0 : _a.state) === "fail" || s.type === "suite" && hasFailed(s.tasks);
225
+ }
226
+ );
176
227
  }
177
228
  function getNames(task) {
178
229
  const names = [task.name];
179
230
  let current = task;
180
231
  while (current == null ? void 0 : current.suite) {
181
232
  current = current.suite;
182
- if (current == null ? void 0 : current.name)
233
+ if (current == null ? void 0 : current.name) {
183
234
  names.unshift(current.name);
235
+ }
184
236
  }
185
- if (current !== task.file)
237
+ if (current !== task.file) {
186
238
  names.unshift(task.file.name);
239
+ }
187
240
  return names;
188
241
  }
189
242
 
190
- export { createFileTask as a, getTests as b, calculateSuiteHash as c, getTasks as d, getSuites as e, hasFailed as f, generateHash as g, hasTests as h, interpretTaskModes as i, getNames as j, createChainable as k, partitionSuiteChildren as p, someTasksAreOnly as s };
243
+ 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, limitConcurrency as m, partitionSuiteChildren as p, someTasksAreOnly as s };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
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-Cjrz1dUg.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-Cjrz1dUg.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-UPEJyRVL.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-UPEJyRVL.js';
5
5
  import { Awaitable } from '@vitest/utils';
6
6
  export { processError } from '@vitest/utils/error';
7
7
  import '@vitest/utils/diff';
8
8
 
9
9
  declare function updateTask(task: Task, runner: VitestRunner): void;
10
10
  declare function startTests(paths: string[], runner: VitestRunner): Promise<File[]>;
11
+ declare function publicCollect(paths: string[], runner: VitestRunner): Promise<File[]>;
11
12
 
12
13
  declare const suite: SuiteAPI;
13
14
  declare const test: TestAPI;
@@ -23,11 +24,11 @@ declare function afterEach<ExtraContext = {}>(fn: SuiteHooks<ExtraContext>['afte
23
24
  declare const onTestFailed: (fn: OnTestFailedHandler) => void;
24
25
  declare const onTestFinished: (fn: OnTestFinishedHandler) => void;
25
26
 
26
- declare function setFn(key: Test | Custom, fn: (() => Awaitable<void>)): void;
27
- declare function getFn<Task = Test | Custom>(key: Task): (() => Awaitable<void>);
27
+ declare function setFn(key: Test | Custom, fn: () => Awaitable<void>): void;
28
+ declare function getFn<Task = Test | Custom>(key: Task): () => Awaitable<void>;
28
29
  declare function setHooks(key: Suite, hooks: SuiteHooks): void;
29
30
  declare function getHooks(key: Suite): SuiteHooks;
30
31
 
31
32
  declare function getCurrentTest<T extends Test | Custom | undefined>(): T;
32
33
 
33
- export { Custom, CustomAPI, File, OnTestFailedHandler, OnTestFinishedHandler, Suite, SuiteAPI, SuiteCollector, SuiteHooks, Task, Test, TestAPI, VitestRunner, afterAll, afterEach, beforeAll, beforeEach, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };
34
+ export { Custom, CustomAPI, File, OnTestFailedHandler, OnTestFinishedHandler, Suite, SuiteAPI, SuiteCollector, SuiteHooks, Task, Test, TestAPI, VitestRunner, afterAll, afterEach, beforeAll, beforeEach, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };
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 { k as createChainable, a as createFileTask, c as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, p as partitionSuiteChildren, h as hasTests, f as hasFailed } from './chunk-tasks.js';
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([fn(...args), new Promise((resolve, reject) => {
61
- var _a;
62
- const timer = setTimeout(() => {
63
- clearTimeout(timer);
64
- reject(new Error(makeTimeoutMsg(isHook, timeout)));
65
- }, timeout);
66
- (_a = timer.unref) == null ? void 0 : _a.call(timer);
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(([prop, value]) => {
98
- const fixtureItem = { value };
99
- if (Array.isArray(value) && value.length >= 2 && isObject(value[1]) && Object.keys(value[1]).some((key) => fixtureOptionKeys.includes(key))) {
100
- Object.assign(fixtureItem, value[1]);
101
- fixtureItem.value = value[0];
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
- fixtureItem.prop = prop;
104
- fixtureItem.isFn = typeof fixtureItem.value === "function";
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(({ prop }) => prop !== fixture.prop && usedProps.includes(prop));
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
- if (!fixtureValueMaps.get(context))
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(({ prop, auto }) => auto || usedProps.includes(prop));
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(`Circular fixture dependency detected: ${fixture.prop} <- ${[...depSet].reverse().map((d) => d.prop).join(" <- ")}`);
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
- const first = args[0];
211
- if (!(first.startsWith("{") && first.endsWith("}")))
212
- throw new Error(`The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "${first}".`);
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(`Rest parameters are not supported in fixtures, received "${last}".`);
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
- function(name, optionsOrFn, optionsOrTest) {
255
- if (getCurrentTest())
256
- throw new Error('Calling the test function inside another test function is not allowed. Please put it inside "describe" or "suite" so it can be properly collected.');
257
- getCurrentSuite().test.fn.call(this, formatName(name), optionsOrFn, optionsOrTest);
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("Cannot use two objects as arguments. Please provide options and a function callback in that order.");
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("Cannot use two functions as arguments. Please use the second argument for options.");
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
- if (shuffle)
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(task2, withTimeout(
359
- withFixtures(handler, context),
360
- (options == null ? void 0 : options.timeout) ?? runner.config.testTimeout
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
- optionsOrFn,
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
- formatName(name2),
386
- { ...this, ...options, handler }
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(formatName(name), factory, mode, this.shuffle, this.each, options);
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(formatTitle(_name, items, idx), () => handler(...items), options) : suite2(formatTitle(_name, items, idx), () => handler(i), options);
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(formatTitle(_name, items, idx), () => handler(...items), options) : test2(formatTitle(_name, items, idx), () => handler(i), options);
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(this, formatName(name), optionsOrFn, optionsOrTest);
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), { truncate: (_b = (_a = runner == null ? void 0 : runner.config) == null ? void 0 : _a.chaiConfig) == null ? void 0 : _b.truncateThreshold });
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(file, config.testNamePattern, hasOnlyTasks, false, config.allowOnly);
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
- if (!((_a = suite.result) == null ? void 0 : _a.hooks))
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(...await Promise.all(hooks.map((fn) => fn(...args))));
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(cleanups.map(async (fn) => {
774
- if (typeof fn !== "function")
775
- return;
776
- await fn();
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, { retry: retryCount, repeats: repeatCount }));
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(suite, test, "beforeEach", runner, [test.context, suite]);
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("Test function is not found. Did you add it using `setFn`?");
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, { retry: retryCount, repeats: repeatCount }));
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 callSuiteHook(suite, test, "afterEach", runner, [test.context, suite]);
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(test, test.onFailed || [], runner.config.sequence.hooks);
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 ((_f = runner.onAfterRunTask) == null ? void 0 : _f.call(runner, test));
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(suite, suite, "beforeAll", runner, [suite]);
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((group) => group.type === "suite");
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((group) => shuffle(group, sequence.seed));
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(new Error(`No test found in suite ${suite.name}`));
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 = limit(runner.config.maxConcurrency));
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(new Error(`No test suite found in file ${file.filepath}`));
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("beforeAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true));
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("afterAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true));
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("beforeEach", withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true));
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("afterEach", withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true));
1196
+ return getCurrentSuite().on(
1197
+ "afterEach",
1198
+ withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true)
1199
+ );
1019
1200
  }
1020
- const onTestFailed = createTestHook("onTestFailed", (test, handler) => {
1021
- test.onFailed || (test.onFailed = []);
1022
- test.onFailed.push(handler);
1023
- });
1024
- const onTestFinished = createTestHook("onTestFinished", (test, handler) => {
1025
- test.onFinished || (test.onFinished = []);
1026
- test.onFinished.push(handler);
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 };
@@ -64,13 +64,18 @@ interface TaskResult {
64
64
  retryCount?: number;
65
65
  repeatCount?: number;
66
66
  }
67
- type TaskResultPack = [id: string, result: TaskResult | undefined, meta: TaskMeta];
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';
71
75
  tasks: Task[];
72
76
  }
73
77
  interface File extends Suite {
78
+ pool?: string;
74
79
  filepath: string;
75
80
  projectName: string | undefined;
76
81
  collectDuration?: number;
@@ -114,6 +119,14 @@ interface TestEachFunction {
114
119
  <T>(cases: ReadonlyArray<T>): EachFunctionReturn<T[]>;
115
120
  (...args: [TemplateStringsArray, ...any]): EachFunctionReturn<any[]>;
116
121
  }
122
+ interface TestForFunctionReturn<Arg, Context> {
123
+ (name: string | Function, fn: (arg: Arg, context: Context) => Awaitable<void>): void;
124
+ (name: string | Function, options: TestOptions, fn: (args: Arg, context: Context) => Awaitable<void>): void;
125
+ }
126
+ interface TestForFunction<ExtraContext> {
127
+ <T>(cases: ReadonlyArray<T>): TestForFunctionReturn<T, ExtendedContext<Test> & ExtraContext>;
128
+ (strings: TemplateStringsArray, ...values: any[]): TestForFunctionReturn<any, ExtendedContext<Test> & ExtraContext>;
129
+ }
117
130
  interface TestCollectorCallable<C = {}> {
118
131
  /**
119
132
  * @deprecated Use options as the second argument instead
@@ -124,6 +137,7 @@ interface TestCollectorCallable<C = {}> {
124
137
  }
125
138
  type ChainableTestAPI<ExtraContext = {}> = ChainableFunction<'concurrent' | 'sequential' | 'only' | 'skip' | 'todo' | 'fails', TestCollectorCallable<ExtraContext>, {
126
139
  each: TestEachFunction;
140
+ for: TestForFunction<ExtraContext>;
127
141
  }>;
128
142
  interface TestOptions {
129
143
  /**
@@ -193,7 +207,7 @@ interface FixtureOptions {
193
207
  }
194
208
  type Use<T> = (value: T) => Promise<void>;
195
209
  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] ? (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);
210
+ 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
211
  type Fixtures<T extends Record<string, any>, ExtraContext = {}> = {
198
212
  [K in keyof T]: Fixture<T, K, ExtraContext & ExtendedContext<Test>> | [Fixture<T, K, ExtraContext & ExtendedContext<Test>>, FixtureOptions?];
199
213
  };
@@ -218,8 +232,14 @@ type HookCleanupCallback = (() => Awaitable<unknown>) | void;
218
232
  interface SuiteHooks<ExtraContext = {}> {
219
233
  beforeAll: HookListener<[Readonly<Suite | File>], HookCleanupCallback>[];
220
234
  afterAll: HookListener<[Readonly<Suite | File>]>[];
221
- beforeEach: HookListener<[ExtendedContext<Test | Custom> & ExtraContext, Readonly<Suite>], HookCleanupCallback>[];
222
- afterEach: HookListener<[ExtendedContext<Test | Custom> & ExtraContext, Readonly<Suite>]>[];
235
+ beforeEach: HookListener<[
236
+ ExtendedContext<Test | Custom> & ExtraContext,
237
+ Readonly<Suite>
238
+ ], HookCleanupCallback>[];
239
+ afterEach: HookListener<[
240
+ ExtendedContext<Test | Custom> & ExtraContext,
241
+ Readonly<Suite>
242
+ ]>[];
223
243
  }
224
244
  interface TaskCustomOptions extends TestOptions {
225
245
  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-Cjrz1dUg.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-Cjrz1dUg.js';
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-UPEJyRVL.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-UPEJyRVL.js';
3
3
  import { DiffOptions } from '@vitest/utils/diff';
4
4
  import '@vitest/utils';
5
5
 
@@ -31,7 +31,7 @@ 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.
@@ -62,6 +62,10 @@ interface VitestRunner {
62
62
  retry: number;
63
63
  repeats: number;
64
64
  }) => unknown;
65
+ /**
66
+ * When the task has finished running, but before cleanup hooks are called
67
+ */
68
+ onTaskFinished?: (test: Task) => unknown;
65
69
  /**
66
70
  * Called after result and state are set.
67
71
  */
@@ -120,6 +124,10 @@ interface VitestRunner {
120
124
  * Publicly available configuration.
121
125
  */
122
126
  config: VitestRunnerConfig;
127
+ /**
128
+ * The name of the current pool. Can affect how stack trace is inferred on the server side.
129
+ */
130
+ pool?: string;
123
131
  }
124
132
 
125
133
  export { type CancelReason, Custom, ExtendedContext, File, SequenceHooks, SequenceSetupFiles, Suite, Task, TaskContext, TaskResultPack, Test, type VitestRunner, type VitestRunnerConfig, type VitestRunnerConstructor, type VitestRunnerImportSource };
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Suite, F as File, T as Task, a as Test, C as Custom } from './tasks-Cjrz1dUg.js';
2
- export { b as ChainableFunction, c as createChainable } from './tasks-Cjrz1dUg.js';
1
+ import { S as Suite, F as File, T as Task, a as Test, C as Custom } from './tasks-UPEJyRVL.js';
2
+ export { b as ChainableFunction, c as createChainable } from './tasks-UPEJyRVL.js';
3
3
  import { Arrayable } from '@vitest/utils';
4
4
 
5
5
  /**
@@ -9,13 +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
+ declare function createFileTask(filepath: string, root: string, projectName: string, pool?: string): File;
13
13
 
14
14
  /**
15
15
  * Partition in tasks groups by consecutive concurrent
16
16
  */
17
17
  declare function partitionSuiteChildren(suite: Suite): Task[][];
18
18
 
19
+ declare function isAtomTest(s: Task): s is Test | Custom;
19
20
  declare function getTests(suite: Arrayable<Task>): (Test | Custom)[];
20
21
  declare function getTasks(tasks?: Arrayable<Task>): Task[];
21
22
  declare function getSuites(suite: Arrayable<Task>): Suite[];
@@ -23,4 +24,9 @@ declare function hasTests(suite: Arrayable<Suite>): boolean;
23
24
  declare function hasFailed(suite: Arrayable<Task>): boolean;
24
25
  declare function getNames(task: Task): string[];
25
26
 
26
- export { calculateSuiteHash, createFileTask, generateHash, getNames, getSuites, getTasks, getTests, hasFailed, hasTests, interpretTaskModes, partitionSuiteChildren, someTasksAreOnly };
27
+ /**
28
+ * Return a function for running multiple async operations with limited concurrency.
29
+ */
30
+ declare function limitConcurrency(concurrency?: number): <Args extends unknown[], T>(func: (...args: Args) => PromiseLike<T> | T, ...args: Args) => Promise<T>;
31
+
32
+ export { calculateSuiteHash, createFileTask, generateHash, getNames, getSuites, getTasks, getTests, hasFailed, hasTests, interpretTaskModes, isAtomTest, limitConcurrency, partitionSuiteChildren, someTasksAreOnly };
package/dist/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- export { c as calculateSuiteHash, k as createChainable, a as createFileTask, g as generateHash, j as getNames, e as getSuites, d as getTasks, b as getTests, f as hasFailed, h as hasTests, i as interpretTaskModes, p as partitionSuiteChildren, s as someTasksAreOnly } from './chunk-tasks.js';
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, m as limitConcurrency, p as partitionSuiteChildren, s as someTasksAreOnly } from './chunk-tasks.js';
2
2
  import '@vitest/utils/error';
3
3
  import 'pathe';
4
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.9",
4
+ "version": "2.0.0",
5
5
  "description": "Vitest test runner",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -38,9 +38,8 @@
38
38
  "dist"
39
39
  ],
40
40
  "dependencies": {
41
- "p-limit": "^5.0.0",
42
41
  "pathe": "^1.1.2",
43
- "@vitest/utils": "2.0.0-beta.9"
42
+ "@vitest/utils": "2.0.0"
44
43
  },
45
44
  "scripts": {
46
45
  "build": "rimraf dist && rollup -c",