import { getSafeTimers, isObject, createDefer, isNegativeNaN, format, objDisplay, objectAttr, toArray, assertTypes, shuffle } from '@vitest/utils';
import { parseSingleStack } from '@vitest/utils/source-map';
import { c as createChainable, b as createFileTask, a as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, l as limitConcurrency, p as partitionSuiteChildren, n as hasTests, m as hasFailed } from './chunk-tasks.js';
import { processError } from '@vitest/utils/error';
export { processError } from '@vitest/utils/error';
import 'pathe';
class PendingError extends Error {
constructor(message, task) {
super(message);
this.message = message;
this.taskId = task.id;
}
code = "VITEST_PENDING";
taskId;
}
const collectorContext = {
tasks: [],
currentSuite: null
};
function collectTask(task) {
var _a;
(_a = collectorContext.currentSuite) == null ? void 0 : _a.tasks.push(task);
}
async function runWithSuite(suite, fn) {
const prev = collectorContext.currentSuite;
collectorContext.currentSuite = suite;
await fn();
collectorContext.currentSuite = prev;
}
function withTimeout(fn, timeout, isHook = false) {
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY) {
return fn;
}
const { setTimeout, clearTimeout } = getSafeTimers();
return function runWithTimeout(...args) {
return Promise.race([
fn(...args),
new Promise((resolve, reject) => {
var _a;
const timer = setTimeout(() => {
clearTimeout(timer);
reject(new Error(makeTimeoutMsg(isHook, timeout)));
}, timeout);
(_a = timer.unref) == null ? void 0 : _a.call(timer);
})
]);
};
}
function createTestContext(test, runner) {
var _a;
const context = function() {
throw new Error("done() callback is deprecated, use promise instead");
};
context.task = test;
context.skip = () => {
test.pending = true;
throw new PendingError("test is skipped; abort execution", test);
};
context.onTestFailed = (fn) => {
test.onFailed || (test.onFailed = []);
test.onFailed.push(fn);
};
context.onTestFinished = (fn) => {
test.onFinished || (test.onFinished = []);
test.onFinished.push(fn);
};
return ((_a = runner.extendTaskContext) == null ? void 0 : _a.call(runner, context)) || context;
}
function makeTimeoutMsg(isHook, timeout) {
return `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.
If this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
}
const fnMap = /* @__PURE__ */ new WeakMap();
const fixtureMap = /* @__PURE__ */ new WeakMap();
const hooksMap = /* @__PURE__ */ new WeakMap();
function setFn(key, fn) {
fnMap.set(key, fn);
}
function getFn(key) {
return fnMap.get(key);
}
function setFixture(key, fixture) {
fixtureMap.set(key, fixture);
}
function getFixture(key) {
return fixtureMap.get(key);
}
function setHooks(key, hooks) {
hooksMap.set(key, hooks);
}
function getHooks(key) {
return hooksMap.get(key);
}
function mergeContextFixtures(fixtures, context = {}) {
const fixtureOptionKeys = ["auto"];
const fixtureArray = Object.entries(fixtures).map(
([prop, value]) => {
const fixtureItem = { value };
if (Array.isArray(value) && value.length >= 2 && isObject(value[1]) && Object.keys(value[1]).some((key) => fixtureOptionKeys.includes(key))) {
Object.assign(fixtureItem, value[1]);
fixtureItem.value = value[0];
}
fixtureItem.prop = prop;
fixtureItem.isFn = typeof fixtureItem.value === "function";
return fixtureItem;
}
);
if (Array.isArray(context.fixtures)) {
context.fixtures = context.fixtures.concat(fixtureArray);
} else {
context.fixtures = fixtureArray;
}
fixtureArray.forEach((fixture) => {
if (fixture.isFn) {
const usedProps = getUsedProps(fixture.value);
if (usedProps.length) {
fixture.deps = context.fixtures.filter(
({ prop }) => prop !== fixture.prop && usedProps.includes(prop)
);
}
}
});
return context;
}
const fixtureValueMaps = /* @__PURE__ */ new Map();
const cleanupFnArrayMap = /* @__PURE__ */ new Map();
async function callFixtureCleanup(context) {
const cleanupFnArray = cleanupFnArrayMap.get(context) ?? [];
for (const cleanup of cleanupFnArray.reverse()) {
await cleanup();
}
cleanupFnArrayMap.delete(context);
}
function withFixtures(fn, testContext) {
return (hookContext) => {
const context = hookContext || testContext;
if (!context) {
return fn({});
}
const fixtures = getFixture(context);
if (!(fixtures == null ? void 0 : fixtures.length)) {
return fn(context);
}
const usedProps = getUsedProps(fn);
const hasAutoFixture = fixtures.some(({ auto }) => auto);
if (!usedProps.length && !hasAutoFixture) {
return fn(context);
}
if (!fixtureValueMaps.get(context)) {
fixtureValueMaps.set(context, /* @__PURE__ */ new Map());
}
const fixtureValueMap = fixtureValueMaps.get(context);
if (!cleanupFnArrayMap.has(context)) {
cleanupFnArrayMap.set(context, []);
}
const cleanupFnArray = cleanupFnArrayMap.get(context);
const usedFixtures = fixtures.filter(
({ prop, auto }) => auto || usedProps.includes(prop)
);
const pendingFixtures = resolveDeps(usedFixtures);
if (!pendingFixtures.length) {
return fn(context);
}
async function resolveFixtures() {
for (const fixture of pendingFixtures) {
if (fixtureValueMap.has(fixture)) {
continue;
}
const resolvedValue = fixture.isFn ? await resolveFixtureFunction(fixture.value, context, cleanupFnArray) : fixture.value;
context[fixture.prop] = resolvedValue;
fixtureValueMap.set(fixture, resolvedValue);
cleanupFnArray.unshift(() => {
fixtureValueMap.delete(fixture);
});
}
}
return resolveFixtures().then(() => fn(context));
};
}
async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
const useFnArgPromise = createDefer();
let isUseFnArgResolved = false;
const fixtureReturn = fixtureFn(context, async (useFnArg) => {
isUseFnArgResolved = true;
useFnArgPromise.resolve(useFnArg);
const useReturnPromise = createDefer();
cleanupFnArray.push(async () => {
useReturnPromise.resolve();
await fixtureReturn;
});
await useReturnPromise;
}).catch((e) => {
if (!isUseFnArgResolved) {
useFnArgPromise.reject(e);
return;
}
throw e;
});
return useFnArgPromise;
}
function resolveDeps(fixtures, depSet = /* @__PURE__ */ new Set(), pendingFixtures = []) {
fixtures.forEach((fixture) => {
if (pendingFixtures.includes(fixture)) {
return;
}
if (!fixture.isFn || !fixture.deps) {
pendingFixtures.push(fixture);
return;
}
if (depSet.has(fixture)) {
throw new Error(
`Circular fixture dependency detected: ${fixture.prop} <- ${[...depSet].reverse().map((d) => d.prop).join(" <- ")}`
);
}
depSet.add(fixture);
resolveDeps(fixture.deps, depSet, pendingFixtures);
pendingFixtures.push(fixture);
depSet.clear();
});
return pendingFixtures;
}
function getUsedProps(fn) {
let fnString = fn.toString();
if (/__async\(this, (?:null|arguments|\[[_0-9, ]*\]), function\*/.test(fnString)) {
fnString = fnString.split("__async(this,")[1];
}
const match = fnString.match(/[^(]*\(([^)]*)/);
if (!match) {
return [];
}
const args = splitByComma(match[1]);
if (!args.length) {
return [];
}
let first = args[0];
if ("__VITEST_FIXTURE_INDEX__" in fn) {
first = args[fn.__VITEST_FIXTURE_INDEX__];
if (!first) {
return [];
}
}
if (!(first.startsWith("{") && first.endsWith("}"))) {
throw new Error(
`The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "${first}".`
);
}
const _first = first.slice(1, -1).replace(/\s/g, "");
const props = splitByComma(_first).map((prop) => {
return prop.replace(/:.*|=.*/g, "");
});
const last = props.at(-1);
if (last && last.startsWith("...")) {
throw new Error(
`Rest parameters are not supported in fixtures, received "${last}".`
);
}
return props;
}
function splitByComma(s) {
const result = [];
const stack = [];
let start = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] === "{" || s[i] === "[") {
stack.push(s[i] === "{" ? "}" : "]");
} else if (s[i] === stack[stack.length - 1]) {
stack.pop();
} else if (!stack.length && s[i] === ",") {
const token = s.substring(start, i).trim();
if (token) {
result.push(token);
}
start = i + 1;
}
}
const lastToken = s.substring(start).trim();
if (lastToken) {
result.push(lastToken);
}
return result;
}
let _test;
function setCurrentTest(test) {
_test = test;
}
function getCurrentTest() {
return _test;
}
const suite = createSuite();
const test = createTest(function(name, optionsOrFn, optionsOrTest) {
if (getCurrentTest()) {
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.'
);
}
getCurrentSuite().test.fn.call(
this,
formatName(name),
optionsOrFn,
optionsOrTest
);
});
const describe = suite;
const it = test;
let runner;
let defaultSuite;
let currentTestFilepath;
function assert(condition, message) {
if (!condition) {
throw new Error(`Vitest failed to find ${message}. This is a bug in Vitest. Please, open an issue with reproduction.`);
}
}
function getDefaultSuite() {
assert(defaultSuite, "the default suite");
return defaultSuite;
}
function getTestFilepath() {
return currentTestFilepath;
}
function getRunner() {
assert(runner, "the runner");
return runner;
}
function createDefaultSuite(runner2) {
const config = runner2.config.sequence;
const api = config.shuffle ? suite.shuffle : suite;
return api("", { concurrent: config.concurrent }, () => {
});
}
function clearCollectorContext(filepath, currentRunner) {
if (!defaultSuite) {
defaultSuite = createDefaultSuite(currentRunner);
}
runner = currentRunner;
currentTestFilepath = filepath;
collectorContext.tasks.length = 0;
defaultSuite.clear();
collectorContext.currentSuite = defaultSuite;
}
function getCurrentSuite() {
const currentSuite = collectorContext.currentSuite || defaultSuite;
assert(currentSuite, "the current suite");
return currentSuite;
}
function createSuiteHooks() {
return {
beforeAll: [],
afterAll: [],
beforeEach: [],
afterEach: []
};
}
function parseArguments(optionsOrFn, optionsOrTest) {
let options = {};
let fn = () => {
};
if (typeof optionsOrTest === "object") {
if (typeof optionsOrFn === "object") {
throw new TypeError(
"Cannot use two objects as arguments. Please provide options and a function callback in that order."
);
}
options = optionsOrTest;
} else if (typeof optionsOrTest === "number") {
options = { timeout: optionsOrTest };
} else if (typeof optionsOrFn === "object") {
options = optionsOrFn;
}
if (typeof optionsOrFn === "function") {
if (typeof optionsOrTest === "function") {
throw new TypeError(
"Cannot use two functions as arguments. Please use the second argument for options."
);
}
fn = optionsOrFn;
} else if (typeof optionsOrTest === "function") {
fn = optionsOrTest;
}
return {
options,
handler: fn
};
}
function createSuiteCollector(name, factory = () => {
}, mode, shuffle, each, suiteOptions) {
const tasks = [];
const factoryQueue = [];
let suite2;
initSuite(true);
const task = function(name2 = "", options = {}) {
const task2 = {
id: "",
name: name2,
suite: void 0,
each: options.each,
fails: options.fails,
context: void 0,
type: "custom",
file: void 0,
retry: options.retry ?? runner.config.retry,
repeats: options.repeats,
mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run",
meta: options.meta ?? /* @__PURE__ */ Object.create(null)
};
const handler = options.handler;
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent) {
task2.concurrent = true;
}
if (shuffle) {
task2.shuffle = true;
}
const context = createTestContext(task2, runner);
Object.defineProperty(task2, "context", {
value: context,
enumerable: false
});
setFixture(context, options.fixtures);
if (handler) {
setFn(
task2,
withTimeout(
withAwaitAsyncAssetions(withFixtures(handler, context), task2),
(options == null ? void 0 : options.timeout) ?? runner.config.testTimeout
)
);
}
if (runner.config.includeTaskLocation) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = 15;
const error = new Error("stacktrace").stack;
Error.stackTraceLimit = limit;
const stack = findTestFileStackTrace(error, task2.each ?? false);
if (stack) {
task2.location = stack;
}
}
tasks.push(task2);
return task2;
};
const test2 = createTest(function(name2, optionsOrFn, optionsOrTest) {
let { options, handler } = parseArguments(optionsOrFn, optionsOrTest);
if (typeof suiteOptions === "object") {
options = Object.assign({}, suiteOptions, options);
}
options.concurrent = this.concurrent || !this.sequential && (options == null ? void 0 : options.concurrent);
options.sequential = this.sequential || !this.concurrent && (options == null ? void 0 : options.sequential);
const test3 = task(formatName(name2), {
...this,
...options,
handler
});
test3.type = "test";
});
const collector = {
type: "collector",
name,
mode,
options: suiteOptions,
test: test2,
tasks,
collect,
task,
clear,
on: addHook
};
function addHook(name2, ...fn) {
getHooks(suite2)[name2].push(...fn);
}
function initSuite(includeLocation) {
if (typeof suiteOptions === "number") {
suiteOptions = { timeout: suiteOptions };
}
suite2 = {
id: "",
type: "suite",
name,
mode,
each,
file: void 0,
shuffle,
tasks: [],
meta: /* @__PURE__ */ Object.create(null),
concurrent: suiteOptions == null ? void 0 : suiteOptions.concurrent
};
if (runner && includeLocation && runner.config.includeTaskLocation) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = 15;
const error = new Error("stacktrace").stack;
Error.stackTraceLimit = limit;
const stack = findTestFileStackTrace(error, suite2.each ?? false);
if (stack) {
suite2.location = stack;
}
}
setHooks(suite2, createSuiteHooks());
}
function clear() {
tasks.length = 0;
factoryQueue.length = 0;
initSuite(false);
}
async function collect(file) {
if (!file) {
throw new TypeError("File is required to collect tasks.");
}
factoryQueue.length = 0;
if (factory) {
await runWithSuite(collector, () => factory(test2));
}
const allChildren = [];
for (const i of [...factoryQueue, ...tasks]) {
allChildren.push(i.type === "collector" ? await i.collect(file) : i);
}
suite2.file = file;
suite2.tasks = allChildren;
allChildren.forEach((task2) => {
task2.suite = suite2;
task2.file = file;
});
return suite2;
}
collectTask(collector);
return collector;
}
function withAwaitAsyncAssetions(fn, task) {
return async (...args) => {
await fn(...args);
if (task.promises) {
const result = await Promise.allSettled(task.promises);
const errors = result.map((r) => r.status === "rejected" ? r.reason : void 0).filter(Boolean);
if (errors.length) {
throw errors;
}
}
};
}
function createSuite() {
function suiteFn(name, factoryOrOptions, optionsOrFactory = {}) {
const mode = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run";
const currentSuite = collectorContext.currentSuite || defaultSuite;
let { options, handler: factory } = parseArguments(
factoryOrOptions,
optionsOrFactory
);
const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false;
const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false;
if (currentSuite == null ? void 0 : currentSuite.options) {
options = { ...currentSuite.options, ...options };
}
const isConcurrent = isConcurrentSpecified || options.concurrent && !isSequentialSpecified;
const isSequential = isSequentialSpecified || options.sequential && !isConcurrentSpecified;
options.concurrent = isConcurrent && !isSequential;
options.sequential = isSequential && !isConcurrent;
return createSuiteCollector(
formatName(name),
factory,
mode,
this.shuffle,
this.each,
options
);
}
suiteFn.each = function(cases, ...args) {
const suite2 = this.withContext();
this.setContext("each", true);
if (Array.isArray(cases) && args.length) {
cases = formatTemplateString(cases, args);
}
return (name, optionsOrFn, fnOrOptions) => {
const _name = formatName(name);
const arrayOnlyCases = cases.every(Array.isArray);
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
const fnFirst = typeof optionsOrFn === "function";
cases.forEach((i, idx) => {
const items = Array.isArray(i) ? i : [i];
if (fnFirst) {
if (arrayOnlyCases) {
suite2(
formatTitle(_name, items, idx),
() => handler(...items),
options
);
} else {
suite2(formatTitle(_name, items, idx), () => handler(i), options);
}
} else {
if (arrayOnlyCases) {
suite2(formatTitle(_name, items, idx), options, () => handler(...items));
} else {
suite2(formatTitle(_name, items, idx), options, () => handler(i));
}
}
});
this.setContext("each", void 0);
};
};
suiteFn.skipIf = (condition) => condition ? suite.skip : suite;
suiteFn.runIf = (condition) => condition ? suite : suite.skip;
return createChainable(
["concurrent", "sequential", "shuffle", "skip", "only", "todo"],
suiteFn
);
}
function createTaskCollector(fn, context) {
const taskFn = fn;
taskFn.each = function(cases, ...args) {
const test2 = this.withContext();
this.setContext("each", true);
if (Array.isArray(cases) && args.length) {
cases = formatTemplateString(cases, args);
}
return (name, optionsOrFn, fnOrOptions) => {
const _name = formatName(name);
const arrayOnlyCases = cases.every(Array.isArray);
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
const fnFirst = typeof optionsOrFn === "function";
cases.forEach((i, idx) => {
const items = Array.isArray(i) ? i : [i];
if (fnFirst) {
if (arrayOnlyCases) {
test2(
formatTitle(_name, items, idx),
() => handler(...items),
options
);
} else {
test2(formatTitle(_name, items, idx), () => handler(i), options);
}
} else {
if (arrayOnlyCases) {
test2(formatTitle(_name, items, idx), options, () => handler(...items));
} else {
test2(formatTitle(_name, items, idx), options, () => handler(i));
}
}
});
this.setContext("each", void 0);
};
};
taskFn.for = function(cases, ...args) {
const test2 = this.withContext();
if (Array.isArray(cases) && args.length) {
cases = formatTemplateString(cases, args);
}
return (name, optionsOrFn, fnOrOptions) => {
const _name = formatName(name);
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
cases.forEach((item, idx) => {
const handlerWrapper = (ctx) => handler(item, ctx);
handlerWrapper.__VITEST_FIXTURE_INDEX__ = 1;
handlerWrapper.toString = () => handler.toString();
test2(formatTitle(_name, toArray(item), idx), options, handlerWrapper);
});
};
};
taskFn.skipIf = function(condition) {
return condition ? this.skip : this;
};
taskFn.runIf = function(condition) {
return condition ? this : this.skip;
};
taskFn.extend = function(fixtures) {
const _context = mergeContextFixtures(fixtures, context);
return createTest(function fn2(name, optionsOrFn, optionsOrTest) {
getCurrentSuite().test.fn.call(
this,
formatName(name),
optionsOrFn,
optionsOrTest
);
}, _context);
};
const _test = createChainable(
["concurrent", "sequential", "skip", "only", "todo", "fails"],
taskFn
);
if (context) {
_test.mergeContext(context);
}
return _test;
}
function createTest(fn, context) {
return createTaskCollector(fn, context);
}
function formatName(name) {
return typeof name === "string" ? name : name instanceof Function ? name.name || "<anonymous>" : String(name);
}
function formatTitle(template, items, idx) {
if (template.includes("%#")) {
template = template.replace(/%%/g, "__vitest_escaped_%__").replace(/%#/g, `${idx}`).replace(/__vitest_escaped_%__/g, "%%");
}
const count = template.split("%").length - 1;
if (template.includes("%f")) {
const placeholders = template.match(/%f/g) || [];
placeholders.forEach((_, i) => {
if (isNegativeNaN(items[i]) || Object.is(items[i], -0)) {
let occurrence = 0;
template = template.replace(/%f/g, (match) => {
occurrence++;
return occurrence === i + 1 ? "-%f" : match;
});
}
});
}
let formatted = format(template, ...items.slice(0, count));
if (isObject(items[0])) {
formatted = formatted.replace(
/\$([$\w.]+)/g,
// https://github.com/chaijs/chai/pull/1490
(_, key) => {
var _a, _b;
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
});
}
);
}
return formatted;
}
function formatTemplateString(cases, args) {
const header = cases.join("").trim().replace(/ /g, "").split("\n").map((i) => i.split("|"))[0];
const res = [];
for (let i = 0; i < Math.floor(args.length / header.length); i++) {
const oneCase = {};
for (let j = 0; j < header.length; j++) {
oneCase[header[j]] = args[i * header.length + j];
}
res.push(oneCase);
}
return res;
}
function findTestFileStackTrace(error, each) {
const lines = error.split("\n").slice(1);
for (const line of lines) {
const stack = parseSingleStack(line);
if (stack && stack.file === getTestFilepath()) {
return {
line: stack.line,
/**
* test.each([1, 2])('name')
* ^ leads here, but should
* ^ lead here
* in source maps it's the same boundary, so it just points to the start of it
*/
column: each ? stack.column + 1 : stack.column
};
}
}
}
function getDefaultHookTimeout() {
return getRunner().config.hookTimeout;
}
function beforeAll(fn, timeout) {
assertTypes(fn, '"beforeAll" callback', ["function"]);
return getCurrentSuite().on(
"beforeAll",
withTimeout(fn, timeout ?? getDefaultHookTimeout(), true)
);
}
function afterAll(fn, timeout) {
assertTypes(fn, '"afterAll" callback', ["function"]);
return getCurrentSuite().on(
"afterAll",
withTimeout(fn, timeout ?? getDefaultHookTimeout(), true)
);
}
function beforeEach(fn, timeout) {
assertTypes(fn, '"beforeEach" callback', ["function"]);
return getCurrentSuite().on(
"beforeEach",
withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true)
);
}
function afterEach(fn, timeout) {
assertTypes(fn, '"afterEach" callback', ["function"]);
return getCurrentSuite().on(
"afterEach",
withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true)
);
}
const onTestFailed = createTestHook(
"onTestFailed",
(test, handler, timeout) => {
test.onFailed || (test.onFailed = []);
test.onFailed.push(
withTimeout(handler, timeout ?? getDefaultHookTimeout(), true)
);
}
);
const onTestFinished = createTestHook(
"onTestFinished",
(test, handler, timeout) => {
test.onFinished || (test.onFinished = []);
test.onFinished.push(
withTimeout(handler, timeout ?? getDefaultHookTimeout(), true)
);
}
);
function createTestHook(name, handler) {
return (fn, timeout) => {
assertTypes(fn, `"${name}" callback`, ["function"]);
const current = getCurrentTest();
if (!current) {
throw new Error(`Hook ${name}() can only be called inside a test`);
}
return handler(current, fn, timeout);
};
}
async function runSetupFiles(config, files, runner) {
if (config.sequence.setupFiles === "parallel") {
await Promise.all(
files.map(async (fsPath) => {
await runner.importFile(fsPath, "setup");
})
);
} else {
for (const fsPath of files) {
await runner.importFile(fsPath, "setup");
}
}
}
const now$1 = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
async function collectTests(paths, runner) {
var _a;
const files = [];
const config = runner.config;
for (const filepath of paths) {
const file = createFileTask(filepath, config.root, config.name, runner.pool);
(_a = runner.onCollectStart) == null ? void 0 : _a.call(runner, file);
clearCollectorContext(filepath, runner);
try {
const setupFiles = toArray(config.setupFiles);
if (setupFiles.length) {
const setupStart = now$1();
await runSetupFiles(config, setupFiles, runner);
const setupEnd = now$1();
file.setupDuration = setupEnd - setupStart;
} else {
file.setupDuration = 0;
}
const collectStart = now$1();
await runner.importFile(filepath, "collect");
const defaultTasks = await getDefaultSuite().collect(file);
const fileHooks = createSuiteHooks();
mergeHooks(fileHooks, getHooks(defaultTasks));
for (const c of [...defaultTasks.tasks, ...collectorContext.tasks]) {
if (c.type === "test" || c.type === "custom" || c.type === "suite") {
file.tasks.push(c);
} else if (c.type === "collector") {
const suite = await c.collect(file);
if (suite.name || suite.tasks.length) {
mergeHooks(fileHooks, getHooks(suite));
file.tasks.push(suite);
}
} else {
c;
}
}
setHooks(file, fileHooks);
file.collectDuration = now$1() - collectStart;
} catch (e) {
const error = processError(e);
file.result = {
state: "fail",
errors: [error]
};
}
calculateSuiteHash(file);
file.tasks.forEach((task) => {
var _a2;
if (((_a2 = task.suite) == null ? void 0 : _a2.id) === "") {
delete task.suite;
}
});
const hasOnlyTasks = someTasksAreOnly(file);
interpretTaskModes(
file,
config.testNamePattern,
hasOnlyTasks,
false,
config.allowOnly
);
files.push(file);
}
return files;
}
function mergeHooks(baseHooks, hooks) {
for (const _key in hooks) {
const key = _key;
baseHooks[key].push(...hooks[key]);
}
return baseHooks;
}
const now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
const unixNow = Date.now;
function updateSuiteHookState(suite, name, state, runner) {
var _a;
if (!suite.result) {
suite.result = { state: "run" };
}
if (!((_a = suite.result) == null ? void 0 : _a.hooks)) {
suite.result.hooks = {};
}
const suiteHooks = suite.result.hooks;
if (suiteHooks) {
suiteHooks[name] = state;
updateTask(suite, runner);
}
}
function getSuiteHooks(suite, name, sequence) {
const hooks = getHooks(suite)[name];
if (sequence === "stack" && (name === "afterAll" || name === "afterEach")) {
return hooks.slice().reverse();
}
return hooks;
}
async function callTestHooks(runner, task, hooks, sequence) {
if (sequence === "stack") {
hooks = hooks.slice().reverse();
}
if (sequence === "parallel") {
try {
await Promise.all(hooks.map((fn) => fn(task.result)));
} catch (e) {
failTask(task.result, e, runner.config.diffOptions);
}
} else {
for (const fn of hooks) {
try {
await fn(task.result);
} catch (e) {
failTask(task.result, e, runner.config.diffOptions);
}
}
}
}
async function callSuiteHook(suite, currentTask, name, runner, args) {
const sequence = runner.config.sequence.hooks;
const callbacks = [];
const parentSuite = "filepath" in suite ? null : suite.suite || suite.file;
if (name === "beforeEach" && parentSuite) {
callbacks.push(
...await callSuiteHook(parentSuite, currentTask, name, runner, args)
);
}
updateSuiteHookState(currentTask, name, "run", runner);
const hooks = getSuiteHooks(suite, name, sequence);
if (sequence === "parallel") {
callbacks.push(
...await Promise.all(hooks.map((hook) => hook(...args)))
);
} else {
for (const hook of hooks) {
callbacks.push(await hook(...args));
}
}
updateSuiteHookState(currentTask, name, "pass", runner);
if (name === "afterEach" && parentSuite) {
callbacks.push(
...await callSuiteHook(parentSuite, currentTask, name, runner, args)
);
}
return callbacks;
}
const packs = /* @__PURE__ */ new Map();
let updateTimer;
let previousUpdate;
function updateTask(task, runner) {
packs.set(task.id, [task.result, task.meta]);
const { clearTimeout, setTimeout } = getSafeTimers();
clearTimeout(updateTimer);
updateTimer = setTimeout(() => {
previousUpdate = sendTasksUpdate(runner);
}, 10);
}
async function sendTasksUpdate(runner) {
var _a;
const { clearTimeout } = getSafeTimers();
clearTimeout(updateTimer);
await previousUpdate;
if (packs.size) {
const taskPacks = Array.from(packs).map(([id, task]) => {
return [id, task[0], task[1]];
});
const p = (_a = runner.onTaskUpdate) == null ? void 0 : _a.call(runner, taskPacks);
packs.clear();
return p;
}
}
async function callCleanupHooks(cleanups) {
await Promise.all(
cleanups.map(async (fn) => {
if (typeof fn !== "function") {
return;
}
await fn();
})
);
}
async function runTest(test, runner) {
var _a, _b, _c, _d, _e, _f, _g;
await ((_a = runner.onBeforeRunTask) == null ? void 0 : _a.call(runner, test));
if (test.mode !== "run") {
return;
}
if (((_b = test.result) == null ? void 0 : _b.state) === "fail") {
updateTask(test, runner);
return;
}
const start = now();
test.result = {
state: "run",
startTime: unixNow(),
retryCount: 0
};
updateTask(test, runner);
setCurrentTest(test);
const suite = test.suite || test.file;
const repeats = test.repeats ?? 0;
for (let repeatCount = 0; repeatCount <= repeats; repeatCount++) {
const retry = test.retry ?? 0;
for (let retryCount = 0; retryCount <= retry; retryCount++) {
let beforeEachCleanups = [];
try {
await ((_c = runner.onBeforeTryTask) == null ? void 0 : _c.call(runner, test, {
retry: retryCount,
repeats: repeatCount
}));
test.result.repeatCount = repeatCount;
beforeEachCleanups = await callSuiteHook(
suite,
test,
"beforeEach",
runner,
[test.context, suite]
);
if (runner.runTask) {
await runner.runTask(test);
} else {
const fn = getFn(test);
if (!fn) {
throw new Error(
"Test function is not found. Did you add it using `setFn`?"
);
}
await fn();
}
await ((_d = runner.onAfterTryTask) == null ? void 0 : _d.call(runner, test, {
retry: retryCount,
repeats: repeatCount
}));
if (test.result.state !== "fail") {
if (!test.repeats) {
test.result.state = "pass";
} else if (test.repeats && retry === retryCount) {
test.result.state = "pass";
}
}
} catch (e) {
failTask(test.result, e, runner.config.diffOptions);
}
if (test.pending || ((_e = test.result) == null ? void 0 : _e.state) === "skip") {
test.mode = "skip";
test.result = { state: "skip" };
updateTask(test, runner);
setCurrentTest(void 0);
return;
}
try {
await ((_f = runner.onTaskFinished) == null ? void 0 : _f.call(runner, test));
} catch (e) {
failTask(test.result, e, runner.config.diffOptions);
}
try {
await callSuiteHook(suite, test, "afterEach", runner, [
test.context,
suite
]);
await callCleanupHooks(beforeEachCleanups);
await callFixtureCleanup(test.context);
} catch (e) {
failTask(test.result, e, runner.config.diffOptions);
}
await callTestHooks(runner, test, test.onFinished || [], "stack");
if (test.result.state === "fail") {
await callTestHooks(
runner,
test,
test.onFailed || [],
runner.config.sequence.hooks
);
}
delete test.onFailed;
delete test.onFinished;
if (test.result.state === "pass") {
break;
}
if (retryCount < retry) {
test.result.state = "run";
test.result.retryCount = (test.result.retryCount ?? 0) + 1;
}
updateTask(test, runner);
}
}
if (test.fails) {
if (test.result.state === "pass") {
const error = processError(new Error("Expect test to fail"));
test.result.state = "fail";
test.result.errors = [error];
} else {
test.result.state = "pass";
test.result.errors = void 0;
}
}
setCurrentTest(void 0);
test.result.duration = now() - start;
await ((_g = runner.onAfterRunTask) == null ? void 0 : _g.call(runner, test));
updateTask(test, runner);
}
function failTask(result, err, diffOptions) {
if (err instanceof PendingError) {
result.state = "skip";
return;
}
result.state = "fail";
const errors = Array.isArray(err) ? err : [err];
for (const e of errors) {
const error = processError(e, diffOptions);
result.errors ?? (result.errors = []);
result.errors.push(error);
}
}
function markTasksAsSkipped(suite, runner) {
suite.tasks.forEach((t) => {
t.mode = "skip";
t.result = { ...t.result, state: "skip" };
updateTask(t, runner);
if (t.type === "suite") {
markTasksAsSkipped(t, runner);
}
});
}
async function runSuite(suite, runner) {
var _a, _b, _c, _d;
await ((_a = runner.onBeforeRunSuite) == null ? void 0 : _a.call(runner, suite));
if (((_b = suite.result) == null ? void 0 : _b.state) === "fail") {
markTasksAsSkipped(suite, runner);
updateTask(suite, runner);
return;
}
const start = now();
suite.result = {
state: "run",
startTime: unixNow()
};
updateTask(suite, runner);
let beforeAllCleanups = [];
if (suite.mode === "skip") {
suite.result.state = "skip";
} else if (suite.mode === "todo") {
suite.result.state = "todo";
} else {
try {
try {
beforeAllCleanups = await callSuiteHook(
suite,
suite,
"beforeAll",
runner,
[suite]
);
} catch (e) {
markTasksAsSkipped(suite, runner);
throw e;
}
if (runner.runSuite) {
await runner.runSuite(suite);
} else {
for (let tasksGroup of partitionSuiteChildren(suite)) {
if (tasksGroup[0].concurrent === true) {
await Promise.all(tasksGroup.map((c) => runSuiteChild(c, runner)));
} else {
const { sequence } = runner.config;
if (sequence.shuffle || suite.shuffle) {
const suites = tasksGroup.filter(
(group) => group.type === "suite"
);
const tests = tasksGroup.filter((group) => group.type === "test");
const groups = shuffle([suites, tests], sequence.seed);
tasksGroup = groups.flatMap(
(group) => shuffle(group, sequence.seed)
);
}
for (const c of tasksGroup) {
await runSuiteChild(c, runner);
}
}
}
}
} catch (e) {
failTask(suite.result, e, runner.config.diffOptions);
}
try {
await callSuiteHook(suite, suite, "afterAll", runner, [suite]);
await callCleanupHooks(beforeAllCleanups);
} catch (e) {
failTask(suite.result, e, runner.config.diffOptions);
}
if (suite.mode === "run") {
if (!runner.config.passWithNoTests && !hasTests(suite)) {
suite.result.state = "fail";
if (!((_c = suite.result.errors) == null ? void 0 : _c.length)) {
const error = processError(
new Error(`No test found in suite ${suite.name}`)
);
suite.result.errors = [error];
}
} else if (hasFailed(suite)) {
suite.result.state = "fail";
} else {
suite.result.state = "pass";
}
}
updateTask(suite, runner);
suite.result.duration = now() - start;
await ((_d = runner.onAfterRunSuite) == null ? void 0 : _d.call(runner, suite));
}
}
let limitMaxConcurrency;
async function runSuiteChild(c, runner) {
if (c.type === "test" || c.type === "custom") {
return limitMaxConcurrency(() => runTest(c, runner));
} else if (c.type === "suite") {
return runSuite(c, runner);
}
}
async function runFiles(files, runner) {
var _a, _b;
limitMaxConcurrency ?? (limitMaxConcurrency = limitConcurrency(runner.config.maxConcurrency));
for (const file of files) {
if (!file.tasks.length && !runner.config.passWithNoTests) {
if (!((_b = (_a = file.result) == null ? void 0 : _a.errors) == null ? void 0 : _b.length)) {
const error = processError(
new Error(`No test suite found in file ${file.filepath}`)
);
file.result = {
state: "fail",
errors: [error]
};
}
}
await runSuite(file, runner);
}
}
async function startTests(paths, runner) {
var _a, _b, _c, _d;
await ((_a = runner.onBeforeCollect) == null ? void 0 : _a.call(runner, paths));
const files = await collectTests(paths, runner);
await ((_b = runner.onCollected) == null ? void 0 : _b.call(runner, files));
await ((_c = runner.onBeforeRunFiles) == null ? void 0 : _c.call(runner, files));
await runFiles(files, runner);
await ((_d = runner.onAfterRunFiles) == null ? void 0 : _d.call(runner, files));
await sendTasksUpdate(runner);
return files;
}
async function publicCollect(paths, runner) {
var _a, _b;
await ((_a = runner.onBeforeCollect) == null ? void 0 : _a.call(runner, paths));
const files = await collectTests(paths, runner);
await ((_b = runner.onCollected) == null ? void 0 : _b.call(runner, files));
return files;
}
export { afterAll, afterEach, beforeAll, beforeEach, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };