@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 +4 -2
- package/dist/chunk-tasks.js +33 -16
- package/dist/index.d.ts +2 -2
- package/dist/index.js +116 -46
- package/dist/{tasks.d-C-iOiT8j.d.ts → tasks.d-D2GKpdwQ.d.ts} +2 -2
- package/dist/types.d.ts +1 -1
- package/dist/utils.d.ts +10 -6
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @vitest/runner
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://npmx.dev/package/@vitest/runner)
|
|
4
4
|
|
|
5
|
-
|
|
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)
|
package/dist/chunk-tasks.js
CHANGED
|
@@ -249,28 +249,45 @@ function limitConcurrency(concurrency = Infinity) {
|
|
|
249
249
|
tail = head && tail;
|
|
250
250
|
}
|
|
251
251
|
};
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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 (
|
|
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
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
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-
|
|
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-
|
|
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
|
|
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.
|
|
146
|
-
this._suiteContexts.set(TestFixtures.
|
|
151
|
+
if (!this._suiteContexts.has(TestFixtures._workerContextSuite)) {
|
|
152
|
+
this._suiteContexts.set(TestFixtures._workerContextSuite, Object.create(null));
|
|
147
153
|
}
|
|
148
|
-
return this._suiteContexts.get(TestFixtures.
|
|
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,
|
|
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
|
-
|
|
449
|
-
if (
|
|
450
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
2037
|
-
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
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
|
|
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-
|
|
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-
|
|
2
|
-
export { ag as ChainableFunction, ah as createChainable } from './tasks.d-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
+
"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://
|
|
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.
|
|
47
|
+
"@vitest/utils": "4.1.0-beta.6"
|
|
43
48
|
},
|
|
44
49
|
"scripts": {
|
|
45
50
|
"build": "premove dist && rollup -c",
|