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 || "" : 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 };