@vitest/runner 4.1.0-beta.4 → 4.1.0-beta.6

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @vitest/runner
2
2
 
3
- Vitest mechanism to collect and run tasks.
3
+ [![NPM version](https://img.shields.io/npm/v/@vitest/runner?color=a1b858&label=)](https://npmx.dev/package/@vitest/runner)
4
4
 
5
- [GitHub](https://github.com/vitest-dev/vitest) | [Documentation](https://vitest.dev/advanced/runner)
5
+ Vitest mechanism to collect and run tests.
6
+
7
+ [GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/runner) | [Documentation](https://vitest.dev/api/advanced/runner)
@@ -249,28 +249,45 @@ function limitConcurrency(concurrency = Infinity) {
249
249
  tail = head && tail;
250
250
  }
251
251
  };
252
- return (func, ...args) => {
253
- // Create a promise chain that:
254
- // 1. Waits for its turn in the task queue (if necessary).
255
- // 2. Runs the task.
256
- // 3. Allows the next pending task (if any) to run.
252
+ const acquire = () => {
253
+ let released = false;
254
+ const release = () => {
255
+ if (!released) {
256
+ released = true;
257
+ finish();
258
+ }
259
+ };
260
+ if (count++ < concurrency) {
261
+ return release;
262
+ }
257
263
  return new Promise((resolve) => {
258
- if (count++ < concurrency) {
259
- // No need to queue if fewer than maxConcurrency tasks are running.
260
- resolve();
261
- } else if (tail) {
264
+ if (tail) {
262
265
  // There are pending tasks, so append to the queue.
263
- tail = tail[1] = [resolve];
266
+ tail = tail[1] = [() => resolve(release)];
264
267
  } else {
265
268
  // No other pending tasks, initialize the queue with a new tail and head.
266
- head = tail = [resolve];
269
+ head = tail = [() => resolve(release)];
270
+ }
271
+ });
272
+ };
273
+ const limiterFn = (func, ...args) => {
274
+ function run(release) {
275
+ try {
276
+ const result = func(...args);
277
+ if (result instanceof Promise) {
278
+ return result.finally(release);
279
+ }
280
+ release();
281
+ return Promise.resolve(result);
282
+ } catch (error) {
283
+ release();
284
+ return Promise.reject(error);
267
285
  }
268
- }).then(() => {
269
- // Running func here ensures that even a non-thenable result or an
270
- // immediately thrown error gets wrapped into a Promise.
271
- return func(...args);
272
- }).finally(finish);
286
+ }
287
+ const release = acquire();
288
+ return release instanceof Promise ? release.then(run) : run(release);
273
289
  };
290
+ return Object.assign(limiterFn, { acquire });
274
291
  }
275
292
 
276
293
  /**
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as TestArtifact, a as Test, S as Suite, b as SuiteHooks, F as FileSpecification, V as VitestRunner, c as File, d as TaskUpdateEvent, e as Task, f as TestAPI, g as SuiteAPI, h as SuiteCollector } from './tasks.d-C-iOiT8j.js';
2
- export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, m as FailureScreenshotArtifact, n as Fixture, o as FixtureFn, p as FixtureOptions, q as Fixtures, I as ImportDuration, r as InferFixturesTypes, O as OnTestFailedHandler, s as OnTestFinishedHandler, R as Retry, t as RunMode, u as RuntimeContext, v as SequenceHooks, w as SequenceSetupFiles, x as SerializableRetry, y as SuiteFactory, z as SuiteOptions, D as TaskBase, E as TaskCustomOptions, G as TaskEventPack, H as TaskHook, J as TaskMeta, K as TaskPopulated, L as TaskResult, M as TaskResultPack, N as TaskState, P as TestAnnotation, Q as TestAnnotationArtifact, U as TestAnnotationLocation, W as TestArtifactBase, X as TestArtifactLocation, Y as TestArtifactRegistry, Z as TestAttachment, _ as TestContext, $ as TestFunction, a0 as TestOptions, a1 as TestTagDefinition, a2 as TestTags, a3 as Use, a4 as VisualRegressionArtifact, a5 as VitestRunnerConfig, a6 as VitestRunnerConstructor, a7 as VitestRunnerImportSource, a8 as afterAll, a9 as afterEach, aa as aroundAll, ab as aroundEach, ac as beforeAll, ad as beforeEach, ae as onTestFailed, af as onTestFinished } from './tasks.d-C-iOiT8j.js';
1
+ import { T as TestArtifact, a as Test, S as Suite, b as SuiteHooks, F as FileSpecification, V as VitestRunner, c as File, d as TaskUpdateEvent, e as Task, f as TestAPI, g as SuiteAPI, h as SuiteCollector } from './tasks.d-D2GKpdwQ.js';
2
+ export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, m as FailureScreenshotArtifact, n as Fixture, o as FixtureFn, p as FixtureOptions, q as Fixtures, I as ImportDuration, r as InferFixturesTypes, O as OnTestFailedHandler, s as OnTestFinishedHandler, R as Retry, t as RunMode, u as RuntimeContext, v as SequenceHooks, w as SequenceSetupFiles, x as SerializableRetry, y as SuiteFactory, z as SuiteOptions, D as TaskBase, E as TaskCustomOptions, G as TaskEventPack, H as TaskHook, J as TaskMeta, K as TaskPopulated, L as TaskResult, M as TaskResultPack, N as TaskState, P as TestAnnotation, Q as TestAnnotationArtifact, U as TestAnnotationLocation, W as TestArtifactBase, X as TestArtifactLocation, Y as TestArtifactRegistry, Z as TestAttachment, _ as TestContext, $ as TestFunction, a0 as TestOptions, a1 as TestTagDefinition, a2 as TestTags, a3 as Use, a4 as VisualRegressionArtifact, a5 as VitestRunnerConfig, a6 as VitestRunnerConstructor, a7 as VitestRunnerImportSource, a8 as afterAll, a9 as afterEach, aa as aroundAll, ab as aroundEach, ac as beforeAll, ad as beforeEach, ae as onTestFailed, af as onTestFinished } from './tasks.d-D2GKpdwQ.js';
3
3
  import { Awaitable } from '@vitest/utils';
4
4
  import '@vitest/utils/diff';
5
5
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { processError } from '@vitest/utils/error';
2
- import { isObject, filterOutComments, createDefer, assertTypes, toArray, isNegativeNaN, unique, objectAttr, shuffle } from '@vitest/utils/helpers';
2
+ import { isObject, filterOutComments, ordinal, createDefer, assertTypes, toArray, isNegativeNaN, unique, objectAttr, shuffle } from '@vitest/utils/helpers';
3
3
  import { getSafeTimers } from '@vitest/utils/timers';
4
4
  import { format, formatRegExp, objDisplay } from '@vitest/utils/display';
5
5
  import { w as getChainableContext, a as createChainable, v as validateTags, e as createTaskName, x as createNoTagsError, f as findTestFileStackTrace, d as createTagsFilter, b as createFileTask, c as calculateSuiteHash, u as someTasksAreOnly, q as interpretTaskModes, s as limitConcurrency, t as partitionSuiteChildren, p as hasTests, o as hasFailed } from './chunk-tasks.js';
@@ -27,6 +27,12 @@ class TestRunAbortError extends Error {
27
27
  class FixtureDependencyError extends Error {
28
28
  name = "FixtureDependencyError";
29
29
  }
30
+ class FixtureAccessError extends Error {
31
+ name = "FixtureAccessError";
32
+ }
33
+ class FixtureParseError extends Error {
34
+ name = "FixtureParseError";
35
+ }
30
36
  class AroundHookSetupError extends Error {
31
37
  name = "AroundHookSetupError";
32
38
  }
@@ -83,7 +89,7 @@ class TestFixtures {
83
89
  "file",
84
90
  "worker"
85
91
  ];
86
- static _workerContextSymbol = Symbol("workerContext");
92
+ static _workerContextSuite = { type: "worker" };
87
93
  static clearDefinitions() {
88
94
  TestFixtures._definitions.length = 0;
89
95
  }
@@ -142,10 +148,10 @@ class TestFixtures {
142
148
  return this._suiteContexts.get(file);
143
149
  }
144
150
  getWorkerContext() {
145
- if (!this._suiteContexts.has(TestFixtures._workerContextSymbol)) {
146
- this._suiteContexts.set(TestFixtures._workerContextSymbol, Object.create(null));
151
+ if (!this._suiteContexts.has(TestFixtures._workerContextSuite)) {
152
+ this._suiteContexts.set(TestFixtures._workerContextSuite, Object.create(null));
147
153
  }
148
- return this._suiteContexts.get(TestFixtures._workerContextSymbol);
154
+ return this._suiteContexts.get(TestFixtures._workerContextSuite);
149
155
  }
150
156
  parseUserFixtures(runner, userFixtures, supportNonTest, registrations = new Map(this._registrations)) {
151
157
  const errors = [];
@@ -442,25 +448,35 @@ function resolveDeps(usedFixtures, registrations, depSet = new Set(), pendingFix
442
448
  });
443
449
  return pendingFixtures;
444
450
  }
445
- function validateSuiteHook(fn, hook, error) {
446
- const usedProps = getUsedProps(fn);
451
+ function validateSuiteHook(fn, hook, suiteError) {
452
+ const usedProps = getUsedProps(fn, {
453
+ sourceError: suiteError,
454
+ suiteHook: hook
455
+ });
447
456
  if (usedProps.size) {
448
- console.warn(`The ${hook} hook uses fixtures "${[...usedProps].join("\", \"")}", but has no access to context. Did you forget to call it as "test.${hook}()" instead of "${hook}()"? This will throw an error in a future major. See https://vitest.dev/guide/test-context#suite-level-hooks`);
449
- if (error) {
450
- const processor = globalThis.__vitest_worker__?.onFilterStackTrace || ((s) => s || "");
451
- const stack = processor(error.stack || "");
452
- console.warn(stack);
457
+ const error = new FixtureAccessError(`The ${hook} hook uses fixtures "${[...usedProps].join("\", \"")}", but has no access to context. ` + `Did you forget to call it as "test.${hook}()" instead of "${hook}()"?\n` + `If you used internal "suite" task as the first argument previously, access it in the second argument instead. ` + `See https://vitest.dev/guide/test-context#suite-level-hooks`);
458
+ if (suiteError) {
459
+ error.stack = suiteError.stack?.replace(suiteError.message, error.message);
453
460
  }
461
+ throw error;
454
462
  }
455
463
  }
456
464
  const kPropsSymbol = Symbol("$vitest:fixture-props");
465
+ const kPropNamesSymbol = Symbol("$vitest:fixture-prop-names");
457
466
  function configureProps(fn, options) {
458
467
  Object.defineProperty(fn, kPropsSymbol, {
459
468
  value: options,
460
469
  enumerable: false
461
470
  });
462
471
  }
463
- function getUsedProps(fn) {
472
+ function memoProps(fn, props) {
473
+ fn[kPropNamesSymbol] = props;
474
+ return props;
475
+ }
476
+ function getUsedProps(fn, { sourceError, suiteHook } = {}) {
477
+ if (kPropNamesSymbol in fn) {
478
+ return fn[kPropNamesSymbol];
479
+ }
464
480
  const { index: fixturesIndex = 0, original: implementation = fn } = kPropsSymbol in fn ? fn[kPropsSymbol] : {};
465
481
  let fnString = filterOutComments(implementation.toString());
466
482
  // match lowered async function and strip it off
@@ -473,18 +489,23 @@ function getUsedProps(fn) {
473
489
  }
474
490
  const match = fnString.match(/[^(]*\(([^)]*)/);
475
491
  if (!match) {
476
- return new Set();
492
+ return memoProps(fn, new Set());
477
493
  }
478
494
  const args = splitByComma(match[1]);
479
495
  if (!args.length) {
480
- return new Set();
496
+ return memoProps(fn, new Set());
481
497
  }
482
498
  const fixturesArgument = args[fixturesIndex];
483
499
  if (!fixturesArgument) {
484
- return new Set();
500
+ return memoProps(fn, new Set());
485
501
  }
486
502
  if (!(fixturesArgument[0] === "{" && fixturesArgument.endsWith("}"))) {
487
- throw new Error(`The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "${fixturesArgument}".`);
503
+ const ordinalArgument = ordinal(fixturesIndex + 1);
504
+ const error = new FixtureParseError(`The ${ordinalArgument} argument inside a fixture must use object destructuring pattern, e.g. ({ task } => {}). ` + `Instead, received "${fixturesArgument}".` + `${suiteHook ? ` If you used internal "suite" task as the ${ordinalArgument} argument previously, access it in the ${ordinal(fixturesIndex + 2)} argument instead.` : ""}`);
505
+ if (sourceError) {
506
+ error.stack = sourceError.stack?.replace(sourceError.message, error.message);
507
+ }
508
+ throw error;
488
509
  }
489
510
  const _first = fixturesArgument.slice(1, -1).replace(/\s/g, "");
490
511
  const props = splitByComma(_first).map((prop) => {
@@ -492,9 +513,13 @@ function getUsedProps(fn) {
492
513
  });
493
514
  const last = props.at(-1);
494
515
  if (last && last.startsWith("...")) {
495
- throw new Error(`Rest parameters are not supported in fixtures, received "${last}".`);
516
+ const error = new FixtureParseError(`Rest parameters are not supported in fixtures, received "${last}".`);
517
+ if (sourceError) {
518
+ error.stack = sourceError.stack?.replace(sourceError.message, error.message);
519
+ }
520
+ throw error;
496
521
  }
497
- return new Set(props);
522
+ return memoProps(fn, new Set(props));
498
523
  }
499
524
  function splitByComma(s) {
500
525
  const result = [];
@@ -1106,7 +1131,7 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
1106
1131
  const stackTraceError = new Error("STACK_TRACE_ERROR");
1107
1132
  Error.stackTraceLimit = limit;
1108
1133
  if (handler) {
1109
- setFn(task, withTimeout(withAwaitAsyncAssertions(withFixtures(handler, { context }), task), timeout, false, stackTraceError, (_, error) => abortIfTimeout([context], error)));
1134
+ setFn(task, withTimeout(withCancel(withAwaitAsyncAssertions(withFixtures(handler, { context }), task), task.context.signal), timeout, false, stackTraceError, (_, error) => abortIfTimeout([context], error)));
1110
1135
  }
1111
1136
  if (runner.config.includeTaskLocation) {
1112
1137
  const error = stackTraceError.stack;
@@ -1640,6 +1665,23 @@ catch (error) {
1640
1665
  });
1641
1666
  });
1642
1667
  }
1668
+ function withCancel(fn, signal) {
1669
+ return (function runWithCancel(...args) {
1670
+ return new Promise((resolve, reject) => {
1671
+ signal.addEventListener("abort", () => reject(signal.reason));
1672
+ try {
1673
+ const result = fn(...args);
1674
+ if (typeof result === "object" && result != null && typeof result.then === "function") {
1675
+ result.then(resolve, reject);
1676
+ } else {
1677
+ resolve(result);
1678
+ }
1679
+ } catch (error) {
1680
+ reject(error);
1681
+ }
1682
+ });
1683
+ });
1684
+ }
1643
1685
  const abortControllers = new WeakMap();
1644
1686
  function abortIfTimeout([context], error) {
1645
1687
  if (context) {
@@ -1818,6 +1860,7 @@ function mergeHooks(baseHooks, hooks) {
1818
1860
  const now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
1819
1861
  const unixNow = Date.now;
1820
1862
  const { clearTimeout, setTimeout } = getSafeTimers();
1863
+ let limitMaxConcurrency;
1821
1864
  /**
1822
1865
  * Normalizes retry configuration to extract individual values.
1823
1866
  * Handles both number and object forms.
@@ -1891,14 +1934,14 @@ async function callTestHooks(runner, test, hooks, sequence) {
1891
1934
  };
1892
1935
  if (sequence === "parallel") {
1893
1936
  try {
1894
- await Promise.all(hooks.map((fn) => fn(test.context)));
1937
+ await Promise.all(hooks.map((fn) => limitMaxConcurrency(() => fn(test.context))));
1895
1938
  } catch (e) {
1896
1939
  failTask(test.result, e, runner.config.diffOptions);
1897
1940
  }
1898
1941
  } else {
1899
1942
  for (const fn of hooks) {
1900
1943
  try {
1901
- await fn(test.context);
1944
+ await limitMaxConcurrency(() => fn(test.context));
1902
1945
  } catch (e) {
1903
1946
  failTask(test.result, e, runner.config.diffOptions);
1904
1947
  }
@@ -1920,7 +1963,9 @@ async function callSuiteHook(suite, currentTask, name, runner, args) {
1920
1963
  updateSuiteHookState(currentTask, name, "run", runner);
1921
1964
  }
1922
1965
  async function runHook(hook) {
1923
- return getBeforeHookCleanupCallback(hook, await hook(...args), name === "beforeEach" ? args[0] : undefined);
1966
+ return limitMaxConcurrency(async () => {
1967
+ return getBeforeHookCleanupCallback(hook, await hook(...args), name === "beforeEach" ? args[0] : undefined);
1968
+ });
1924
1969
  }
1925
1970
  if (sequence === "parallel") {
1926
1971
  callbacks.push(...await Promise.all(hooks.map((hook) => runHook(hook))));
@@ -1964,6 +2009,7 @@ async function callAroundHooks(runInner, options) {
1964
2009
  await runInner();
1965
2010
  return;
1966
2011
  }
2012
+ const hookErrors = [];
1967
2013
  const createTimeoutPromise = (timeout, phase, stackTraceError) => {
1968
2014
  let timer;
1969
2015
  let timedout = false;
@@ -2000,6 +2046,8 @@ async function callAroundHooks(runInner, options) {
2000
2046
  let useCalled = false;
2001
2047
  let setupTimeout;
2002
2048
  let teardownTimeout;
2049
+ let setupLimitConcurrencyRelease;
2050
+ let teardownLimitConcurrencyRelease;
2003
2051
  // Promise that resolves when use() is called (setup phase complete)
2004
2052
  let resolveUseCalled;
2005
2053
  const useCalledPromise = new Promise((resolve) => {
@@ -2032,21 +2080,16 @@ async function callAroundHooks(runInner, options) {
2032
2080
  resolveUseCalled();
2033
2081
  // Setup phase completed - clear setup timer
2034
2082
  setupTimeout.clear();
2083
+ setupLimitConcurrencyRelease?.();
2035
2084
  // Run inner hooks - don't time this against our teardown timeout
2036
- let nextError;
2037
- try {
2038
- await runNextHook(index + 1);
2039
- } catch (value) {
2040
- nextError = { value };
2041
- }
2085
+ await runNextHook(index + 1).catch((e) => hookErrors.push(e));
2086
+ teardownLimitConcurrencyRelease = await limitMaxConcurrency.acquire();
2042
2087
  // Start teardown timer after inner hooks complete - only times this hook's teardown code
2043
2088
  teardownTimeout = createTimeoutPromise(timeout, "teardown", stackTraceError);
2044
2089
  // Signal that use() is returning (teardown phase starting)
2045
2090
  resolveUseReturned();
2046
- if (nextError) {
2047
- throw nextError.value;
2048
- }
2049
2091
  };
2092
+ setupLimitConcurrencyRelease = await limitMaxConcurrency.acquire();
2050
2093
  // Start setup timeout
2051
2094
  setupTimeout = createTimeoutPromise(timeout, "setup", stackTraceError);
2052
2095
  (async () => {
@@ -2058,6 +2101,9 @@ async function callAroundHooks(runInner, options) {
2058
2101
  resolveHookComplete();
2059
2102
  } catch (error) {
2060
2103
  rejectHookComplete(error);
2104
+ } finally {
2105
+ setupLimitConcurrencyRelease?.();
2106
+ teardownLimitConcurrencyRelease?.();
2061
2107
  }
2062
2108
  })();
2063
2109
  // Wait for either: use() to be called OR hook to complete (error) OR setup timeout
@@ -2068,6 +2114,7 @@ async function callAroundHooks(runInner, options) {
2068
2114
  setupTimeout.promise
2069
2115
  ]);
2070
2116
  } finally {
2117
+ setupLimitConcurrencyRelease?.();
2071
2118
  setupTimeout.clear();
2072
2119
  }
2073
2120
  // Wait for use() to return (inner hooks complete) OR hook to complete (error during inner hooks)
@@ -2077,10 +2124,14 @@ async function callAroundHooks(runInner, options) {
2077
2124
  try {
2078
2125
  await Promise.race([hookCompletePromise, teardownTimeout?.promise]);
2079
2126
  } finally {
2127
+ teardownLimitConcurrencyRelease?.();
2080
2128
  teardownTimeout?.clear();
2081
2129
  }
2082
2130
  };
2083
- await runNextHook(0);
2131
+ await runNextHook(0).catch((e) => hookErrors.push(e));
2132
+ if (hookErrors.length > 0) {
2133
+ throw hookErrors;
2134
+ }
2084
2135
  }
2085
2136
  async function callAroundAllHooks(suite, runSuiteInner) {
2086
2137
  await callAroundHooks(runSuiteInner, {
@@ -2168,14 +2219,14 @@ async function callCleanupHooks(runner, cleanups) {
2168
2219
  if (typeof fn !== "function") {
2169
2220
  return;
2170
2221
  }
2171
- await fn();
2222
+ await limitMaxConcurrency(() => fn());
2172
2223
  }));
2173
2224
  } else {
2174
2225
  for (const fn of cleanups) {
2175
2226
  if (typeof fn !== "function") {
2176
2227
  continue;
2177
2228
  }
2178
- await fn();
2229
+ await limitMaxConcurrency(() => fn());
2179
2230
  }
2180
2231
  }
2181
2232
  }
@@ -2241,24 +2292,20 @@ async function runTest(test, runner) {
2241
2292
  test.result.repeatCount = repeatCount;
2242
2293
  beforeEachCleanups = await $("test.beforeEach", () => callSuiteHook(suite, test, "beforeEach", runner, [test.context, suite]));
2243
2294
  if (runner.runTask) {
2244
- await $("test.callback", () => runner.runTask(test));
2295
+ await $("test.callback", () => limitMaxConcurrency(() => runner.runTask(test)));
2245
2296
  } else {
2246
2297
  const fn = getFn(test);
2247
2298
  if (!fn) {
2248
2299
  throw new Error("Test function is not found. Did you add it using `setFn`?");
2249
2300
  }
2250
- await $("test.callback", () => fn());
2301
+ await $("test.callback", () => limitMaxConcurrency(() => fn()));
2251
2302
  }
2252
2303
  await runner.onAfterTryTask?.(test, {
2253
2304
  retry: retryCount,
2254
2305
  repeats: repeatCount
2255
2306
  });
2256
2307
  if (test.result.state !== "fail") {
2257
- if (!test.repeats) {
2258
- test.result.state = "pass";
2259
- } else if (test.repeats && retry === retryCount) {
2260
- test.result.state = "pass";
2261
- }
2308
+ test.result.state = "pass";
2262
2309
  }
2263
2310
  } catch (e) {
2264
2311
  failTask(test.result, e, runner.config.diffOptions);
@@ -2358,6 +2405,11 @@ function failTask(result, err, diffOptions) {
2358
2405
  result.pending = true;
2359
2406
  return;
2360
2407
  }
2408
+ if (err instanceof TestRunAbortError) {
2409
+ result.state = "skip";
2410
+ result.note = err.message;
2411
+ return;
2412
+ }
2361
2413
  result.state = "fail";
2362
2414
  const errors = Array.isArray(err) ? err : [err];
2363
2415
  for (const e of errors) {
@@ -2379,6 +2431,22 @@ function markTasksAsSkipped(suite, runner) {
2379
2431
  }
2380
2432
  });
2381
2433
  }
2434
+ function markPendingTasksAsSkipped(suite, runner, note) {
2435
+ suite.tasks.forEach((t) => {
2436
+ if (!t.result || t.result.state === "run") {
2437
+ t.mode = "skip";
2438
+ t.result = {
2439
+ ...t.result,
2440
+ state: "skip",
2441
+ note
2442
+ };
2443
+ updateTask("test-cancel", t, runner);
2444
+ }
2445
+ if (t.type === "suite") {
2446
+ markPendingTasksAsSkipped(t, runner, note);
2447
+ }
2448
+ });
2449
+ }
2382
2450
  async function runSuite(suite, runner) {
2383
2451
  await runner.onBeforeRunSuite?.(suite);
2384
2452
  if (suite.result?.state === "fail") {
@@ -2479,11 +2547,10 @@ async function runSuite(suite, runner) {
2479
2547
  updateTask("suite-finished", suite, runner);
2480
2548
  }
2481
2549
  }
2482
- let limitMaxConcurrency;
2483
2550
  async function runSuiteChild(c, runner) {
2484
2551
  const $ = runner.trace;
2485
2552
  if (c.type === "test") {
2486
- return limitMaxConcurrency(() => $("run.test", {
2553
+ return $("run.test", {
2487
2554
  "vitest.test.id": c.id,
2488
2555
  "vitest.test.name": c.name,
2489
2556
  "vitest.test.mode": c.mode,
@@ -2491,7 +2558,7 @@ async function runSuiteChild(c, runner) {
2491
2558
  "code.file.path": c.file.filepath,
2492
2559
  "code.line.number": c.location?.line,
2493
2560
  "code.column.number": c.location?.column
2494
- }, () => runTest(c, runner)));
2561
+ }, () => runTest(c, runner));
2495
2562
  } else if (c.type === "suite") {
2496
2563
  return $("run.suite", {
2497
2564
  "vitest.suite.id": c.id,
@@ -2536,7 +2603,10 @@ async function startTests(specs, runner) {
2536
2603
  runner.cancel = (reason) => {
2537
2604
  // We intentionally create only one error since there is only one test run that can be cancelled
2538
2605
  const error = new TestRunAbortError("The test run was aborted by the user.", reason);
2539
- getRunningTests().forEach((test) => abortContextSignal(test.context, error));
2606
+ getRunningTests().forEach((test) => {
2607
+ abortContextSignal(test.context, error);
2608
+ markPendingTasksAsSkipped(test.file, runner, error.message);
2609
+ });
2540
2610
  return cancel?.(reason);
2541
2611
  };
2542
2612
  if (!workerRunners.has(runner)) {
@@ -213,7 +213,7 @@ declare class TestFixtures {
213
213
  private static _builtinFixtures;
214
214
  private static _fixtureOptionKeys;
215
215
  private static _fixtureScopes;
216
- private static _workerContextSymbol;
216
+ private static _workerContextSuite;
217
217
  static clearDefinitions(): void;
218
218
  static getWorkerContexts(): Record<string, any>[];
219
219
  static getFileContexts(file: File): Record<string, any>[];
@@ -605,7 +605,7 @@ interface TaskEventData {
605
605
  artifact?: TestArtifact | undefined;
606
606
  }
607
607
  type TaskEventPack = [id: string, event: TaskUpdateEvent, data: TaskEventData | undefined];
608
- type TaskUpdateEvent = "test-failed-early" | "suite-failed-early" | "test-prepare" | "test-finished" | "test-retried" | "suite-prepare" | "suite-finished" | "before-hook-start" | "before-hook-end" | "after-hook-start" | "after-hook-end" | "test-annotation" | "test-artifact";
608
+ type TaskUpdateEvent = "test-failed-early" | "suite-failed-early" | "test-prepare" | "test-finished" | "test-retried" | "test-cancel" | "suite-prepare" | "suite-finished" | "before-hook-start" | "before-hook-end" | "after-hook-start" | "after-hook-end" | "test-annotation" | "test-artifact";
609
609
  interface Suite extends TaskBase {
610
610
  type: "suite";
611
611
  /**
package/dist/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, m as FailureScreenshotArtifact, c as File, F as FileSpecification, n as Fixture, o as FixtureFn, p as FixtureOptions, q as Fixtures, I as ImportDuration, r as InferFixturesTypes, O as OnTestFailedHandler, s as OnTestFinishedHandler, R as Retry, t as RunMode, u as RuntimeContext, v as SequenceHooks, w as SequenceSetupFiles, x as SerializableRetry, S as Suite, g as SuiteAPI, h as SuiteCollector, y as SuiteFactory, b as SuiteHooks, z as SuiteOptions, e as Task, D as TaskBase, E as TaskCustomOptions, G as TaskEventPack, H as TaskHook, J as TaskMeta, K as TaskPopulated, L as TaskResult, M as TaskResultPack, N as TaskState, d as TaskUpdateEvent, a as Test, f as TestAPI, P as TestAnnotation, Q as TestAnnotationArtifact, U as TestAnnotationLocation, T as TestArtifact, W as TestArtifactBase, X as TestArtifactLocation, Y as TestArtifactRegistry, Z as TestAttachment, _ as TestContext, $ as TestFunction, a0 as TestOptions, a1 as TestTagDefinition, a2 as TestTags, a3 as Use, a4 as VisualRegressionArtifact, V as VitestRunner, a5 as VitestRunnerConfig, a6 as VitestRunnerConstructor, a7 as VitestRunnerImportSource } from './tasks.d-C-iOiT8j.js';
1
+ export { A as AfterAllListener, i as AfterEachListener, j as AroundAllListener, k as AroundEachListener, B as BeforeAllListener, l as BeforeEachListener, C as CancelReason, m as FailureScreenshotArtifact, c as File, F as FileSpecification, n as Fixture, o as FixtureFn, p as FixtureOptions, q as Fixtures, I as ImportDuration, r as InferFixturesTypes, O as OnTestFailedHandler, s as OnTestFinishedHandler, R as Retry, t as RunMode, u as RuntimeContext, v as SequenceHooks, w as SequenceSetupFiles, x as SerializableRetry, S as Suite, g as SuiteAPI, h as SuiteCollector, y as SuiteFactory, b as SuiteHooks, z as SuiteOptions, e as Task, D as TaskBase, E as TaskCustomOptions, G as TaskEventPack, H as TaskHook, J as TaskMeta, K as TaskPopulated, L as TaskResult, M as TaskResultPack, N as TaskState, d as TaskUpdateEvent, a as Test, f as TestAPI, P as TestAnnotation, Q as TestAnnotationArtifact, U as TestAnnotationLocation, T as TestArtifact, W as TestArtifactBase, X as TestArtifactLocation, Y as TestArtifactRegistry, Z as TestAttachment, _ as TestContext, $ as TestFunction, a0 as TestOptions, a1 as TestTagDefinition, a2 as TestTags, a3 as Use, a4 as VisualRegressionArtifact, V as VitestRunner, a5 as VitestRunnerConfig, a6 as VitestRunnerConstructor, a7 as VitestRunnerImportSource } from './tasks.d-D2GKpdwQ.js';
2
2
  import '@vitest/utils';
3
3
  import '@vitest/utils/diff';
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Suite, c as File, e as Task, a1 as TestTagDefinition, a5 as VitestRunnerConfig, a as Test } from './tasks.d-C-iOiT8j.js';
2
- export { ag as ChainableFunction, ah as createChainable } from './tasks.d-C-iOiT8j.js';
1
+ import { S as Suite, c as File, e as Task, a1 as TestTagDefinition, a5 as VitestRunnerConfig, a as Test } from './tasks.d-D2GKpdwQ.js';
2
+ export { ag as ChainableFunction, ah as createChainable } from './tasks.d-D2GKpdwQ.js';
3
3
  import { ParsedStack, Arrayable } from '@vitest/utils';
4
4
  import '@vitest/utils/diff';
5
5
 
@@ -19,13 +19,17 @@ declare function createFileTask(filepath: string, root: string, projectName: str
19
19
  declare function generateFileHash(file: string, projectName: string | undefined): string;
20
20
  declare function findTestFileStackTrace(testFilePath: string, error: string): ParsedStack | undefined;
21
21
 
22
- /**
23
- * Return a function for running multiple async operations with limited concurrency.
24
- */
25
- declare function limitConcurrency(concurrency?: number): <
22
+ interface ConcurrencyLimiter extends ConcurrencyLimiterFn {
23
+ acquire: () => (() => void) | Promise<() => void>;
24
+ }
25
+ type ConcurrencyLimiterFn = <
26
26
  Args extends unknown[],
27
27
  T
28
28
  >(func: (...args: Args) => PromiseLike<T> | T, ...args: Args) => Promise<T>;
29
+ /**
30
+ * Return a function for running multiple async operations with limited concurrency.
31
+ */
32
+ declare function limitConcurrency(concurrency?: number): ConcurrencyLimiter;
29
33
 
30
34
  /**
31
35
  * Partition in tasks groups by consecutive concurrent
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@vitest/runner",
3
3
  "type": "module",
4
- "version": "4.1.0-beta.4",
4
+ "version": "4.1.0-beta.6",
5
5
  "description": "Vitest test runner",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
8
- "homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/runner#readme",
8
+ "homepage": "https://vitest.dev/api/advanced/runner",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/vitest-dev/vitest.git",
@@ -14,6 +14,11 @@
14
14
  "bugs": {
15
15
  "url": "https://github.com/vitest-dev/vitest/issues"
16
16
  },
17
+ "keywords": [
18
+ "vitest",
19
+ "test",
20
+ "test-runner"
21
+ ],
17
22
  "sideEffects": true,
18
23
  "exports": {
19
24
  ".": {
@@ -39,7 +44,7 @@
39
44
  ],
40
45
  "dependencies": {
41
46
  "pathe": "^2.0.3",
42
- "@vitest/utils": "4.1.0-beta.4"
47
+ "@vitest/utils": "4.1.0-beta.6"
43
48
  },
44
49
  "scripts": {
45
50
  "build": "premove dist && rollup -c",