diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-14 14:46:37 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-14 14:46:37 -0800 |
| commit | afa87af01c79a9baa539f2992d32154d2a4739bd (patch) | |
| tree | 92c7416db734270a2fee1d72ee9cc119379ff8e1 /vanilla/node_modules/@vitest/runner/dist/index.js | |
| parent | 3b927e84d200402281f68181cd4253bc77e5528d (diff) | |
| download | neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.gz neko-afa87af01c79a9baa539f2992d32154d2a4739bd.tar.bz2 neko-afa87af01c79a9baa539f2992d32154d2a4739bd.zip | |
task: delete vanilla js prototype\n\n- Removed vanilla/ directory and web/dist/vanilla directory\n- Updated Makefile, Dockerfile, and CI workflow to remove vanilla references\n- Cleaned up web/web.go to remove vanilla embed and routes\n- Verified build and tests pass\n\nCloses NK-2tcnmq
Diffstat (limited to 'vanilla/node_modules/@vitest/runner/dist/index.js')
| -rw-r--r-- | vanilla/node_modules/@vitest/runner/dist/index.js | 2114 |
1 files changed, 0 insertions, 2114 deletions
diff --git a/vanilla/node_modules/@vitest/runner/dist/index.js b/vanilla/node_modules/@vitest/runner/dist/index.js deleted file mode 100644 index c6403f4..0000000 --- a/vanilla/node_modules/@vitest/runner/dist/index.js +++ /dev/null @@ -1,2114 +0,0 @@ -import { processError } from '@vitest/utils/error'; -import { isObject, createDefer, assertTypes, toArray, isNegativeNaN, objectAttr, shuffle } from '@vitest/utils/helpers'; -import { getSafeTimers } from '@vitest/utils/timers'; -import { format, formatRegExp, objDisplay } from '@vitest/utils/display'; -import { c as createChainable, e as createTaskName, f as findTestFileStackTrace, b as createFileTask, a as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, l as limitConcurrency, p as partitionSuiteChildren, r as hasTests, q as hasFailed } from './chunk-tasks.js'; -import '@vitest/utils/source-map'; -import 'pathe'; - -class PendingError extends Error { - code = "VITEST_PENDING"; - taskId; - constructor(message, task, note) { - super(message); - this.message = message; - this.note = note; - this.taskId = task.id; - } -} -class TestRunAbortError extends Error { - name = "TestRunAbortError"; - reason; - constructor(message, reason) { - super(message); - this.reason = reason; - } -} - -// use WeakMap here to make the Test and Suite object serializable -const fnMap = new WeakMap(); -const testFixtureMap = new WeakMap(); -const hooksMap = new WeakMap(); -function setFn(key, fn) { - fnMap.set(key, fn); -} -function getFn(key) { - return fnMap.get(key); -} -function setTestFixture(key, fixture) { - testFixtureMap.set(key, fixture); -} -function getTestFixture(key) { - return testFixtureMap.get(key); -} -function setHooks(key, hooks) { - hooksMap.set(key, hooks); -} -function getHooks(key) { - return hooksMap.get(key); -} - -function mergeScopedFixtures(testFixtures, scopedFixtures) { - const scopedFixturesMap = scopedFixtures.reduce((map, fixture) => { - map[fixture.prop] = fixture; - return map; - }, {}); - const newFixtures = {}; - testFixtures.forEach((fixture) => { - const useFixture = scopedFixturesMap[fixture.prop] || { ...fixture }; - newFixtures[useFixture.prop] = useFixture; - }); - for (const fixtureKep in newFixtures) { - var _fixture$deps; - const fixture = newFixtures[fixtureKep]; - // if the fixture was define before the scope, then its dep - // will reference the original fixture instead of the scope - fixture.deps = (_fixture$deps = fixture.deps) === null || _fixture$deps === void 0 ? void 0 : _fixture$deps.map((dep) => newFixtures[dep.prop]); - } - return Object.values(newFixtures); -} -function mergeContextFixtures(fixtures, context, runner) { - const fixtureOptionKeys = [ - "auto", - "injected", - "scope" - ]; - 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))) { - var _runner$injectValue; - // fixture with options - Object.assign(fixtureItem, value[1]); - const userValue = value[0]; - fixtureItem.value = fixtureItem.injected ? ((_runner$injectValue = runner.injectValue) === null || _runner$injectValue === void 0 ? void 0 : _runner$injectValue.call(runner, prop)) ?? userValue : userValue; - } - fixtureItem.scope = fixtureItem.scope || "test"; - if (fixtureItem.scope === "worker" && !runner.getWorkerContext) { - fixtureItem.scope = "file"; - } - 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; - } - // Update dependencies of fixture functions - 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)); - } - // test can access anything, so we ignore it - if (fixture.scope !== "test") { - var _fixture$deps2; - (_fixture$deps2 = fixture.deps) === null || _fixture$deps2 === void 0 ? void 0 : _fixture$deps2.forEach((dep) => { - if (!dep.isFn) { - // non fn fixtures are always resolved and available to anyone - return; - } - // worker scope can only import from worker scope - if (fixture.scope === "worker" && dep.scope === "worker") { - return; - } - // file scope an import from file and worker scopes - if (fixture.scope === "file" && dep.scope !== "test") { - return; - } - throw new SyntaxError(`cannot use the ${dep.scope} fixture "${dep.prop}" inside the ${fixture.scope} fixture "${fixture.prop}"`); - }); - } - } - }); - return context; -} -const fixtureValueMaps = new Map(); -const cleanupFnArrayMap = new Map(); -async function callFixtureCleanup(context) { - const cleanupFnArray = cleanupFnArrayMap.get(context) ?? []; - for (const cleanup of cleanupFnArray.reverse()) { - await cleanup(); - } - cleanupFnArrayMap.delete(context); -} -function withFixtures(runner, fn, testContext) { - return (hookContext) => { - const context = hookContext || testContext; - if (!context) { - return fn({}); - } - const fixtures = getTestFixture(context); - if (!(fixtures === null || fixtures === void 0 ? 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, 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) { - // fixture could be already initialized during "before" hook - if (fixtureValueMap.has(fixture)) { - continue; - } - const resolvedValue = await resolveFixtureValue(runner, fixture, context, cleanupFnArray); - context[fixture.prop] = resolvedValue; - fixtureValueMap.set(fixture, resolvedValue); - if (fixture.scope === "test") { - cleanupFnArray.unshift(() => { - fixtureValueMap.delete(fixture); - }); - } - } - } - return resolveFixtures().then(() => fn(context)); - }; -} -const globalFixturePromise = new WeakMap(); -function resolveFixtureValue(runner, fixture, context, cleanupFnArray) { - var _runner$getWorkerCont; - const fileContext = getFileContext(context.task.file); - const workerContext = (_runner$getWorkerCont = runner.getWorkerContext) === null || _runner$getWorkerCont === void 0 ? void 0 : _runner$getWorkerCont.call(runner); - if (!fixture.isFn) { - var _fixture$prop; - fileContext[_fixture$prop = fixture.prop] ?? (fileContext[_fixture$prop] = fixture.value); - if (workerContext) { - var _fixture$prop2; - workerContext[_fixture$prop2 = fixture.prop] ?? (workerContext[_fixture$prop2] = fixture.value); - } - return fixture.value; - } - if (fixture.scope === "test") { - return resolveFixtureFunction(fixture.value, context, cleanupFnArray); - } - // in case the test runs in parallel - if (globalFixturePromise.has(fixture)) { - return globalFixturePromise.get(fixture); - } - let fixtureContext; - if (fixture.scope === "worker") { - if (!workerContext) { - throw new TypeError("[@vitest/runner] The worker context is not available in the current test runner. Please, provide the `getWorkerContext` method when initiating the runner."); - } - fixtureContext = workerContext; - } else { - fixtureContext = fileContext; - } - if (fixture.prop in fixtureContext) { - return fixtureContext[fixture.prop]; - } - if (!cleanupFnArrayMap.has(fixtureContext)) { - cleanupFnArrayMap.set(fixtureContext, []); - } - const cleanupFnFileArray = cleanupFnArrayMap.get(fixtureContext); - const promise = resolveFixtureFunction(fixture.value, fixtureContext, cleanupFnFileArray).then((value) => { - fixtureContext[fixture.prop] = value; - globalFixturePromise.delete(fixture); - return value; - }); - globalFixturePromise.set(fixture, promise); - return promise; -} -async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) { - // wait for `use` call to extract fixture value - const useFnArgPromise = createDefer(); - let isUseFnArgResolved = false; - const fixtureReturn = fixtureFn(context, async (useFnArg) => { - // extract `use` argument - isUseFnArgResolved = true; - useFnArgPromise.resolve(useFnArg); - // suspend fixture teardown by holding off `useReturnPromise` resolution until cleanup - const useReturnPromise = createDefer(); - cleanupFnArray.push(async () => { - // start teardown by resolving `use` Promise - useReturnPromise.resolve(); - // wait for finishing teardown - await fixtureReturn; - }); - await useReturnPromise; - }).catch((e) => { - // treat fixture setup error as test failure - if (!isUseFnArgResolved) { - useFnArgPromise.reject(e); - return; - } - // otherwise re-throw to avoid silencing error during cleanup - throw e; - }); - return useFnArgPromise; -} -function resolveDeps(fixtures, depSet = 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 = filterOutComments(fn.toString()); - // match lowered async function and strip it off - // example code on esbuild-try https://esbuild.github.io/try/#YgAwLjI0LjAALS1zdXBwb3J0ZWQ6YXN5bmMtYXdhaXQ9ZmFsc2UAZQBlbnRyeS50cwBjb25zdCBvID0gewogIGYxOiBhc3luYyAoKSA9PiB7fSwKICBmMjogYXN5bmMgKGEpID0+IHt9LAogIGYzOiBhc3luYyAoYSwgYikgPT4ge30sCiAgZjQ6IGFzeW5jIGZ1bmN0aW9uKGEpIHt9LAogIGY1OiBhc3luYyBmdW5jdGlvbiBmZihhKSB7fSwKICBhc3luYyBmNihhKSB7fSwKCiAgZzE6IGFzeW5jICgpID0+IHt9LAogIGcyOiBhc3luYyAoeyBhIH0pID0+IHt9LAogIGczOiBhc3luYyAoeyBhIH0sIGIpID0+IHt9LAogIGc0OiBhc3luYyBmdW5jdGlvbiAoeyBhIH0pIHt9LAogIGc1OiBhc3luYyBmdW5jdGlvbiBnZyh7IGEgfSkge30sCiAgYXN5bmMgZzYoeyBhIH0pIHt9LAoKICBoMTogYXN5bmMgKCkgPT4ge30sCiAgLy8gY29tbWVudCBiZXR3ZWVuCiAgaDI6IGFzeW5jIChhKSA9PiB7fSwKfQ - // __async(this, null, function* - // __async(this, arguments, function* - // __async(this, [_0, _1], function* - if (/__async\((?:this|null), (?:null|arguments|\[[_0-9, ]*\]), function\*/.test(fnString)) { - fnString = fnString.split(/__async\((?:this|null),/)[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[0] === "{" && 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 filterOutComments(s) { - const result = []; - let commentState = "none"; - for (let i = 0; i < s.length; ++i) { - if (commentState === "singleline") { - if (s[i] === "\n") { - commentState = "none"; - } - } else if (commentState === "multiline") { - if (s[i - 1] === "*" && s[i] === "/") { - commentState = "none"; - } - } else if (commentState === "none") { - if (s[i] === "/" && s[i + 1] === "/") { - commentState = "singleline"; - } else if (s[i] === "/" && s[i + 1] === "*") { - commentState = "multiline"; - i += 2; - } else { - result.push(s[i]); - } - } - } - return result.join(""); -} -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.at(-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 tests = []; -function addRunningTest(test) { - tests.push(test); - return () => { - tests.splice(tests.indexOf(test)); - }; -} -function getRunningTests() { - return tests; -} - -function getDefaultHookTimeout() { - return getRunner().config.hookTimeout; -} -const CLEANUP_TIMEOUT_KEY = Symbol.for("VITEST_CLEANUP_TIMEOUT"); -const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE"); -function getBeforeHookCleanupCallback(hook, result, context) { - if (typeof result === "function") { - const timeout = CLEANUP_TIMEOUT_KEY in hook && typeof hook[CLEANUP_TIMEOUT_KEY] === "number" ? hook[CLEANUP_TIMEOUT_KEY] : getDefaultHookTimeout(); - const stackTraceError = CLEANUP_STACK_TRACE_KEY in hook && hook[CLEANUP_STACK_TRACE_KEY] instanceof Error ? hook[CLEANUP_STACK_TRACE_KEY] : undefined; - return withTimeout(result, timeout, true, stackTraceError, (_, error) => { - if (context) { - abortContextSignal(context, error); - } - }); - } -} -/** -* Registers a callback function to be executed once before all tests within the current suite. -* This hook is useful for scenarios where you need to perform setup operations that are common to all tests in a suite, such as initializing a database connection or setting up a test environment. -* -* **Note:** The `beforeAll` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file. -* -* @param {Function} fn - The callback function to be executed before all tests. -* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used. -* @returns {void} -* @example -* ```ts -* // Example of using beforeAll to set up a database connection -* beforeAll(async () => { -* await database.connect(); -* }); -* ``` -*/ -function beforeAll(fn, timeout = getDefaultHookTimeout()) { - assertTypes(fn, "\"beforeAll\" callback", ["function"]); - const stackTraceError = new Error("STACK_TRACE_ERROR"); - return getCurrentSuite().on("beforeAll", Object.assign(withTimeout(fn, timeout, true, stackTraceError), { - [CLEANUP_TIMEOUT_KEY]: timeout, - [CLEANUP_STACK_TRACE_KEY]: stackTraceError - })); -} -/** -* Registers a callback function to be executed once after all tests within the current suite have completed. -* This hook is useful for scenarios where you need to perform cleanup operations after all tests in a suite have run, such as closing database connections or cleaning up temporary files. -* -* **Note:** The `afterAll` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file. -* -* @param {Function} fn - The callback function to be executed after all tests. -* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used. -* @returns {void} -* @example -* ```ts -* // Example of using afterAll to close a database connection -* afterAll(async () => { -* await database.disconnect(); -* }); -* ``` -*/ -function afterAll(fn, timeout) { - assertTypes(fn, "\"afterAll\" callback", ["function"]); - return getCurrentSuite().on("afterAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"))); -} -/** -* Registers a callback function to be executed before each test within the current suite. -* This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables. -* -* **Note:** The `beforeEach` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file. -* -* @param {Function} fn - The callback function to be executed before each test. This function receives an `TestContext` parameter if additional test context is needed. -* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used. -* @returns {void} -* @example -* ```ts -* // Example of using beforeEach to reset a database state -* beforeEach(async () => { -* await database.reset(); -* }); -* ``` -*/ -function beforeEach(fn, timeout = getDefaultHookTimeout()) { - assertTypes(fn, "\"beforeEach\" callback", ["function"]); - const stackTraceError = new Error("STACK_TRACE_ERROR"); - const runner = getRunner(); - return getCurrentSuite().on("beforeEach", Object.assign(withTimeout(withFixtures(runner, fn), timeout ?? getDefaultHookTimeout(), true, stackTraceError, abortIfTimeout), { - [CLEANUP_TIMEOUT_KEY]: timeout, - [CLEANUP_STACK_TRACE_KEY]: stackTraceError - })); -} -/** -* Registers a callback function to be executed after each test within the current suite has completed. -* This hook is useful for scenarios where you need to clean up or reset the test environment after each test runs, such as deleting temporary files, clearing test-specific database entries, or resetting mocked functions. -* -* **Note:** The `afterEach` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file. -* -* @param {Function} fn - The callback function to be executed after each test. This function receives an `TestContext` parameter if additional test context is needed. -* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used. -* @returns {void} -* @example -* ```ts -* // Example of using afterEach to delete temporary files created during a test -* afterEach(async () => { -* await fileSystem.deleteTempFiles(); -* }); -* ``` -*/ -function afterEach(fn, timeout) { - assertTypes(fn, "\"afterEach\" callback", ["function"]); - const runner = getRunner(); - return getCurrentSuite().on("afterEach", withTimeout(withFixtures(runner, fn), timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout)); -} -/** -* Registers a callback function to be executed when a test fails within the current suite. -* This function allows for custom actions to be performed in response to test failures, such as logging, cleanup, or additional diagnostics. -* -* **Note:** The `onTestFailed` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file. -* -* @param {Function} fn - The callback function to be executed upon a test failure. The function receives the test result (including errors). -* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used. -* @throws {Error} Throws an error if the function is not called within a test. -* @returns {void} -* @example -* ```ts -* // Example of using onTestFailed to log failure details -* onTestFailed(({ errors }) => { -* console.log(`Test failed: ${test.name}`, errors); -* }); -* ``` -*/ -const onTestFailed = createTestHook("onTestFailed", (test, handler, timeout) => { - test.onFailed || (test.onFailed = []); - test.onFailed.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout)); -}); -/** -* Registers a callback function to be executed when the current test finishes, regardless of the outcome (pass or fail). -* This function is ideal for performing actions that should occur after every test execution, such as cleanup, logging, or resetting shared resources. -* -* This hook is useful if you have access to a resource in the test itself and you want to clean it up after the test finishes. It is a more compact way to clean up resources than using the combination of `beforeEach` and `afterEach`. -* -* **Note:** The `onTestFinished` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file. -* -* **Note:** The `onTestFinished` hook is not called if the test is canceled with a dynamic `ctx.skip()` call. -* -* @param {Function} fn - The callback function to be executed after a test finishes. The function can receive parameters providing details about the completed test, including its success or failure status. -* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used. -* @throws {Error} Throws an error if the function is not called within a test. -* @returns {void} -* @example -* ```ts -* // Example of using onTestFinished for cleanup -* const db = await connectToDatabase(); -* onTestFinished(async () => { -* await db.disconnect(); -* }); -* ``` -*/ -const onTestFinished = createTestHook("onTestFinished", (test, handler, timeout) => { - test.onFinished || (test.onFinished = []); - test.onFinished.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout)); -}); -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); - }; -} - -/** -* Creates a suite of tests, allowing for grouping and hierarchical organization of tests. -* Suites can contain both tests and other suites, enabling complex test structures. -* -* @param {string} name - The name of the suite, used for identification and reporting. -* @param {Function} fn - A function that defines the tests and suites within this suite. -* @example -* ```ts -* // Define a suite with two tests -* suite('Math operations', () => { -* test('should add two numbers', () => { -* expect(add(1, 2)).toBe(3); -* }); -* -* test('should subtract two numbers', () => { -* expect(subtract(5, 2)).toBe(3); -* }); -* }); -* ``` -* @example -* ```ts -* // Define nested suites -* suite('String operations', () => { -* suite('Trimming', () => { -* test('should trim whitespace from start and end', () => { -* expect(' hello '.trim()).toBe('hello'); -* }); -* }); -* -* suite('Concatenation', () => { -* test('should concatenate two strings', () => { -* expect('hello' + ' ' + 'world').toBe('hello world'); -* }); -* }); -* }); -* ``` -*/ -const suite = createSuite(); -/** -* Defines a test case with a given name and test function. The test function can optionally be configured with test options. -* -* @param {string | Function} name - The name of the test or a function that will be used as a test name. -* @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided. -* @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters. -* @throws {Error} If called inside another test function. -* @example -* ```ts -* // Define a simple test -* test('should add two numbers', () => { -* expect(add(1, 2)).toBe(3); -* }); -* ``` -* @example -* ```ts -* // Define a test with options -* test('should subtract two numbers', { retry: 3 }, () => { -* expect(subtract(5, 2)).toBe(3); -* }); -* ``` -*/ -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); -}); -/** -* Creates a suite of tests, allowing for grouping and hierarchical organization of tests. -* Suites can contain both tests and other suites, enabling complex test structures. -* -* @param {string} name - The name of the suite, used for identification and reporting. -* @param {Function} fn - A function that defines the tests and suites within this suite. -* @example -* ```ts -* // Define a suite with two tests -* describe('Math operations', () => { -* test('should add two numbers', () => { -* expect(add(1, 2)).toBe(3); -* }); -* -* test('should subtract two numbers', () => { -* expect(subtract(5, 2)).toBe(3); -* }); -* }); -* ``` -* @example -* ```ts -* // Define nested suites -* describe('String operations', () => { -* describe('Trimming', () => { -* test('should trim whitespace from start and end', () => { -* expect(' hello '.trim()).toBe('hello'); -* }); -* }); -* -* describe('Concatenation', () => { -* test('should concatenate two strings', () => { -* expect('hello' + ' ' + 'world').toBe('hello world'); -* }); -* }); -* }); -* ``` -*/ -const describe = suite; -/** -* Defines a test case with a given name and test function. The test function can optionally be configured with test options. -* -* @param {string | Function} name - The name of the test or a function that will be used as a test name. -* @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided. -* @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters. -* @throws {Error} If called inside another test function. -* @example -* ```ts -* // Define a simple test -* it('adds two numbers', () => { -* expect(add(1, 2)).toBe(3); -* }); -* ``` -* @example -* ```ts -* // Define a test with options -* it('subtracts two numbers', { retry: 3 }, () => { -* expect(subtract(5, 2)).toBe(3); -* }); -* ``` -*/ -const it = test; -let runner; -let defaultSuite; -let currentTestFilepath; -function assert(condition, message) { - if (!condition) { - throw new Error(`Vitest failed to find ${message}. One of the following is possible:` + "\n- \"vitest\" is imported directly without running \"vitest\" command" + "\n- \"vitest\" is imported inside \"globalSetup\" (to fix this, use \"setupFiles\" instead, because \"globalSetup\" runs in a different context)" + "\n- \"vitest\" is imported inside Vite / Vitest config file" + "\n- Otherwise, it might be a Vitest bug. Please report it to https://github.com/vitest-dev/vitest/issues\n"); - } -} -function getDefaultSuite() { - assert(defaultSuite, "the default suite"); - return defaultSuite; -} -function getRunner() { - assert(runner, "the runner"); - return runner; -} -function createDefaultSuite(runner) { - const config = runner.config.sequence; - const collector = suite("", { concurrent: config.concurrent }, () => {}); - // no parent suite for top-level tests - delete collector.suite; - return collector; -} -function clearCollectorContext(file, currentRunner) { - if (!defaultSuite) { - defaultSuite = createDefaultSuite(currentRunner); - } - defaultSuite.file = file; - runner = currentRunner; - currentTestFilepath = file.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, timeoutOrTest) { - if (timeoutOrTest != null && typeof timeoutOrTest === "object") { - throw new TypeError(`Signature "test(name, fn, { ... })" was deprecated in Vitest 3 and removed in Vitest 4. Please, provide options as a second argument instead.`); - } - let options = {}; - let fn; - // it('', () => {}, 1000) - if (typeof timeoutOrTest === "number") { - options = { timeout: timeoutOrTest }; - } else if (typeof optionsOrFn === "object") { - options = optionsOrFn; - } - if (typeof optionsOrFn === "function") { - if (typeof timeoutOrTest === "function") { - throw new TypeError("Cannot use two functions as arguments. Please use the second argument for options."); - } - fn = optionsOrFn; - } else if (typeof timeoutOrTest === "function") { - fn = timeoutOrTest; - } - return { - options, - handler: fn - }; -} -// implementations -function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions, parentCollectorFixtures) { - const tasks = []; - let suite; - initSuite(true); - const task = function(name = "", options = {}) { - var _collectorContext$cur, _collectorContext$cur2, _collectorContext$cur3; - const timeout = (options === null || options === void 0 ? void 0 : options.timeout) ?? runner.config.testTimeout; - const currentSuite = (_collectorContext$cur = collectorContext.currentSuite) === null || _collectorContext$cur === void 0 ? void 0 : _collectorContext$cur.suite; - const task = { - id: "", - name, - fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur2 = collectorContext.currentSuite) === null || _collectorContext$cur2 === void 0 || (_collectorContext$cur2 = _collectorContext$cur2.file) === null || _collectorContext$cur2 === void 0 ? void 0 : _collectorContext$cur2.fullName), name]), - fullTestName: createTaskName([currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullTestName, name]), - suite: currentSuite, - each: options.each, - fails: options.fails, - context: undefined, - type: "test", - file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur3 = collectorContext.currentSuite) === null || _collectorContext$cur3 === void 0 ? void 0 : _collectorContext$cur3.file), - timeout, - retry: options.retry ?? runner.config.retry, - repeats: options.repeats, - mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run", - meta: options.meta ?? Object.create(null), - annotations: [], - artifacts: [] - }; - const handler = options.handler; - if (task.mode === "run" && !handler) { - task.mode = "todo"; - } - if (options.concurrent || !options.sequential && runner.config.sequence.concurrent) { - task.concurrent = true; - } - task.shuffle = suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.shuffle; - const context = createTestContext(task, runner); - // create test context - Object.defineProperty(task, "context", { - value: context, - enumerable: false - }); - setTestFixture(context, options.fixtures); - // custom can be called from any place, let's assume the limit is 15 stacks - const limit = Error.stackTraceLimit; - Error.stackTraceLimit = 15; - const stackTraceError = new Error("STACK_TRACE_ERROR"); - Error.stackTraceLimit = limit; - if (handler) { - setFn(task, withTimeout(withAwaitAsyncAssertions(withFixtures(runner, handler, context), task), timeout, false, stackTraceError, (_, error) => abortIfTimeout([context], error))); - } - if (runner.config.includeTaskLocation) { - const error = stackTraceError.stack; - const stack = findTestFileStackTrace(currentTestFilepath, error); - if (stack) { - task.location = { - line: stack.line, - column: stack.column - }; - } - } - tasks.push(task); - return task; - }; - const test = createTest(function(name, optionsOrFn, timeoutOrTest) { - let { options, handler } = parseArguments(optionsOrFn, timeoutOrTest); - // inherit repeats, retry, timeout from suite - if (typeof suiteOptions === "object") { - options = Object.assign({}, suiteOptions, options); - } - // inherit concurrent / sequential from suite - options.concurrent = this.concurrent || !this.sequential && (options === null || options === void 0 ? void 0 : options.concurrent); - options.sequential = this.sequential || !this.concurrent && (options === null || options === void 0 ? void 0 : options.sequential); - const test = task(formatName(name), { - ...this, - ...options, - handler - }); - test.type = "test"; - }); - let collectorFixtures = parentCollectorFixtures; - const collector = { - type: "collector", - name, - mode, - suite, - options: suiteOptions, - test, - tasks, - collect, - task, - clear, - on: addHook, - fixtures() { - return collectorFixtures; - }, - scoped(fixtures) { - const parsed = mergeContextFixtures(fixtures, { fixtures: collectorFixtures }, runner); - if (parsed.fixtures) { - collectorFixtures = parsed.fixtures; - } - } - }; - function addHook(name, ...fn) { - getHooks(suite)[name].push(...fn); - } - function initSuite(includeLocation) { - var _collectorContext$cur4, _collectorContext$cur5, _collectorContext$cur6; - if (typeof suiteOptions === "number") { - suiteOptions = { timeout: suiteOptions }; - } - const currentSuite = (_collectorContext$cur4 = collectorContext.currentSuite) === null || _collectorContext$cur4 === void 0 ? void 0 : _collectorContext$cur4.suite; - suite = { - id: "", - type: "suite", - name, - fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur5 = collectorContext.currentSuite) === null || _collectorContext$cur5 === void 0 || (_collectorContext$cur5 = _collectorContext$cur5.file) === null || _collectorContext$cur5 === void 0 ? void 0 : _collectorContext$cur5.fullName), name]), - fullTestName: createTaskName([currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullTestName, name]), - suite: currentSuite, - mode, - each, - file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur6 = collectorContext.currentSuite) === null || _collectorContext$cur6 === void 0 ? void 0 : _collectorContext$cur6.file), - shuffle: suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.shuffle, - tasks: [], - meta: Object.create(null), - concurrent: suiteOptions === null || suiteOptions === void 0 ? 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(currentTestFilepath, error); - if (stack) { - suite.location = { - line: stack.line, - column: stack.column - }; - } - } - setHooks(suite, createSuiteHooks()); - } - function clear() { - tasks.length = 0; - initSuite(false); - } - async function collect(file) { - if (!file) { - throw new TypeError("File is required to collect tasks."); - } - if (factory) { - await runWithSuite(collector, () => factory(test)); - } - const allChildren = []; - for (const i of tasks) { - allChildren.push(i.type === "collector" ? await i.collect(file) : i); - } - suite.tasks = allChildren; - return suite; - } - collectTask(collector); - return collector; -} -function withAwaitAsyncAssertions(fn, task) { - return (async (...args) => { - const fnResult = await fn(...args); - // some async expect will be added to this array, in case user forget to await them - if (task.promises) { - const result = await Promise.allSettled(task.promises); - const errors = result.map((r) => r.status === "rejected" ? r.reason : undefined).filter(Boolean); - if (errors.length) { - throw errors; - } - } - return fnResult; - }); -} -function createSuite() { - function suiteFn(name, factoryOrOptions, optionsOrFactory) { - var _currentSuite$options; - if (getCurrentTest()) { - throw new Error("Calling the suite function inside test function is not allowed. It can be only called at the top level or inside another suite function."); - } - let mode = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run"; - const currentSuite = collectorContext.currentSuite || defaultSuite; - let { options, handler: factory } = parseArguments(factoryOrOptions, optionsOrFactory); - if (mode === "run" && !factory) { - mode = "todo"; - } - const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false; - const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false; - // inherit options from current suite - options = { - ...currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.options, - ...options, - shuffle: this.shuffle ?? options.shuffle ?? (currentSuite === null || currentSuite === void 0 || (_currentSuite$options = currentSuite.options) === null || _currentSuite$options === void 0 ? void 0 : _currentSuite$options.shuffle) ?? (runner === null || runner === void 0 ? void 0 : runner.config.sequence.shuffle) - }; - // inherit concurrent / sequential from suite - 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.each, options, currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fixtures()); - } - suiteFn.each = function(cases, ...args) { - const suite = 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) { - suite(formatTitle(_name, items, idx), handler ? () => handler(...items) : undefined, options.timeout); - } else { - suite(formatTitle(_name, items, idx), handler ? () => handler(i) : undefined, options.timeout); - } - } else { - if (arrayOnlyCases) { - suite(formatTitle(_name, items, idx), options, handler ? () => handler(...items) : undefined); - } else { - suite(formatTitle(_name, items, idx), options, handler ? () => handler(i) : undefined); - } - } - }); - this.setContext("each", undefined); - }; - }; - suiteFn.for = function(cases, ...args) { - 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) => { - suite(formatTitle(name_, toArray(item), idx), options, handler ? () => handler(item) : undefined); - }); - }; - }; - 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 test = 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) { - test(formatTitle(_name, items, idx), handler ? () => handler(...items) : undefined, options.timeout); - } else { - test(formatTitle(_name, items, idx), handler ? () => handler(i) : undefined, options.timeout); - } - } else { - if (arrayOnlyCases) { - test(formatTitle(_name, items, idx), options, handler ? () => handler(...items) : undefined); - } else { - test(formatTitle(_name, items, idx), options, handler ? () => handler(i) : undefined); - } - } - }); - this.setContext("each", undefined); - }; - }; - taskFn.for = function(cases, ...args) { - const test = 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) => { - // monkey-patch handler to allow parsing fixture - const handlerWrapper = handler ? (ctx) => handler(item, ctx) : undefined; - if (handlerWrapper) { - handlerWrapper.__VITEST_FIXTURE_INDEX__ = 1; - handlerWrapper.toString = () => handler.toString(); - } - test(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.scoped = function(fixtures) { - const collector = getCurrentSuite(); - collector.scoped(fixtures); - }; - taskFn.extend = function(fixtures) { - const _context = mergeContextFixtures(fixtures, context || {}, runner); - const originalWrapper = fn; - return createTest(function(name, optionsOrFn, optionsOrTest) { - const collector = getCurrentSuite(); - const scopedFixtures = collector.fixtures(); - const context = { ...this }; - if (scopedFixtures) { - context.fixtures = mergeScopedFixtures(context.fixtures || [], scopedFixtures); - } - originalWrapper.call(context, formatName(name), optionsOrFn, optionsOrTest); - }, _context); - }; - taskFn.beforeEach = beforeEach; - taskFn.afterEach = afterEach; - taskFn.beforeAll = beforeAll; - taskFn.afterAll = afterAll; - 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 : typeof name === "function" ? name.name || "<anonymous>" : String(name); -} -function formatTitle(template, items, idx) { - if (template.includes("%#") || template.includes("%$")) { - // '%#' match index of the test case - template = template.replace(/%%/g, "__vitest_escaped_%__").replace(/%#/g, `${idx}`).replace(/%\$/g, `${idx + 1}`).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)) { - // Replace the i-th occurrence of '%f' with '-%f' - let occurrence = 0; - template = template.replace(/%f/g, (match) => { - occurrence++; - return occurrence === i + 1 ? "-%f" : match; - }); - } - }); - } - const isObjectItem = isObject(items[0]); - function formatAttribute(s) { - return s.replace(/\$([$\w.]+)/g, (_, key) => { - var _runner$config; - const isArrayKey = /^\d+$/.test(key); - if (!isObjectItem && !isArrayKey) { - return `$${key}`; - } - const arrayElement = isArrayKey ? objectAttr(items, key) : undefined; - const value = isObjectItem ? objectAttr(items[0], key, arrayElement) : arrayElement; - return objDisplay(value, { truncate: runner === null || runner === void 0 || (_runner$config = runner.config) === null || _runner$config === void 0 || (_runner$config = _runner$config.chaiConfig) === null || _runner$config === void 0 ? void 0 : _runner$config.truncateThreshold }); - }); - } - let output = ""; - let i = 0; - handleRegexMatch( - template, - formatRegExp, - // format "%" - (match) => { - if (i < count) { - output += format(match[0], items[i++]); - } else { - output += match[0]; - } - }, - // format "$" - (nonMatch) => { - output += formatAttribute(nonMatch); - } - ); - return output; -} -// based on https://github.com/unocss/unocss/blob/2e74b31625bbe3b9c8351570749aa2d3f799d919/packages/autocomplete/src/parse.ts#L11 -function handleRegexMatch(input, regex, onMatch, onNonMatch) { - let lastIndex = 0; - for (const m of input.matchAll(regex)) { - if (lastIndex < m.index) { - onNonMatch(input.slice(lastIndex, m.index)); - } - onMatch(m); - lastIndex = m.index + m[0].length; - } - if (lastIndex < input.length) { - onNonMatch(input.slice(lastIndex)); - } -} -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; -} - -const now$2 = Date.now; -const collectorContext = { - tasks: [], - currentSuite: null -}; -function collectTask(task) { - var _collectorContext$cur; - (_collectorContext$cur = collectorContext.currentSuite) === null || _collectorContext$cur === void 0 ? void 0 : _collectorContext$cur.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, stackTraceError, onTimeout) { - if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY) { - return fn; - } - const { setTimeout, clearTimeout } = getSafeTimers(); - // this function name is used to filter error in test/cli/test/fails.test.ts - return (function runWithTimeout(...args) { - const startTime = now$2(); - const runner = getRunner(); - runner._currentTaskStartTime = startTime; - runner._currentTaskTimeout = timeout; - return new Promise((resolve_, reject_) => { - var _timer$unref; - const timer = setTimeout(() => { - clearTimeout(timer); - rejectTimeoutError(); - }, timeout); - // `unref` might not exist in browser - (_timer$unref = timer.unref) === null || _timer$unref === void 0 ? void 0 : _timer$unref.call(timer); - function rejectTimeoutError() { - const error = makeTimeoutError(isHook, timeout, stackTraceError); - onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout(args, error); - reject_(error); - } - function resolve(result) { - runner._currentTaskStartTime = undefined; - runner._currentTaskTimeout = undefined; - clearTimeout(timer); - // if test/hook took too long in microtask, setTimeout won't be triggered, - // but we still need to fail the test, see - // https://github.com/vitest-dev/vitest/issues/2920 - if (now$2() - startTime >= timeout) { - rejectTimeoutError(); - return; - } - resolve_(result); - } - function reject(error) { - runner._currentTaskStartTime = undefined; - runner._currentTaskTimeout = undefined; - clearTimeout(timer); - reject_(error); - } - // sync test/hook will be caught by try/catch - try { - const result = fn(...args); - // the result is a thenable, we don't wrap this in Promise.resolve - // to avoid creating new promises - if (typeof result === "object" && result != null && typeof result.then === "function") { - result.then(resolve, reject); - } else { - resolve(result); - } - } - // user sync test/hook throws an error -catch (error) { - reject(error); - } - }); - }); -} -const abortControllers = new WeakMap(); -function abortIfTimeout([context], error) { - if (context) { - abortContextSignal(context, error); - } -} -function abortContextSignal(context, error) { - const abortController = abortControllers.get(context); - abortController === null || abortController === void 0 ? void 0 : abortController.abort(error); -} -function createTestContext(test, runner) { - var _runner$extendTaskCon; - const context = function() { - throw new Error("done() callback is deprecated, use promise instead"); - }; - let abortController = abortControllers.get(context); - if (!abortController) { - abortController = new AbortController(); - abortControllers.set(context, abortController); - } - context.signal = abortController.signal; - context.task = test; - context.skip = (condition, note) => { - if (condition === false) { - // do nothing - return undefined; - } - test.result ?? (test.result = { state: "skip" }); - test.result.pending = true; - throw new PendingError("test is skipped; abort execution", test, typeof condition === "string" ? condition : note); - }; - context.annotate = ((message, type, attachment) => { - if (test.result && test.result.state !== "run") { - throw new Error(`Cannot annotate tests outside of the test run. The test "${test.name}" finished running with the "${test.result.state}" state already.`); - } - const annotation = { - message, - type: typeof type === "object" || type === undefined ? "notice" : type - }; - const annotationAttachment = typeof type === "object" ? type : attachment; - if (annotationAttachment) { - annotation.attachment = annotationAttachment; - manageArtifactAttachment(annotation.attachment); - } - return recordAsyncOperation(test, recordArtifact(test, { - type: "internal:annotation", - annotation - }).then(async ({ annotation }) => { - if (!runner.onTestAnnotate) { - throw new Error(`Test runner doesn't support test annotations.`); - } - await finishSendTasksUpdate(runner); - const resolvedAnnotation = await runner.onTestAnnotate(test, annotation); - test.annotations.push(resolvedAnnotation); - return resolvedAnnotation; - })); - }); - context.onTestFailed = (handler, timeout) => { - test.onFailed || (test.onFailed = []); - test.onFailed.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error))); - }; - context.onTestFinished = (handler, timeout) => { - test.onFinished || (test.onFinished = []); - test.onFinished.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error))); - }; - return ((_runner$extendTaskCon = runner.extendTaskContext) === null || _runner$extendTaskCon === void 0 ? void 0 : _runner$extendTaskCon.call(runner, context)) || context; -} -function makeTimeoutError(isHook, timeout, stackTraceError) { - const message = `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.\nIf 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 error = new Error(message); - if (stackTraceError === null || stackTraceError === void 0 ? void 0 : stackTraceError.stack) { - error.stack = stackTraceError.stack.replace(error.message, stackTraceError.message); - } - return error; -} -const fileContexts = new WeakMap(); -function getFileContext(file) { - const context = fileContexts.get(file); - if (!context) { - throw new Error(`Cannot find file context for ${file.name}`); - } - return context; -} -function setFileContext(file, context) { - fileContexts.set(file, context); -} - -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(specs, runner) { - const files = []; - const config = runner.config; - const $ = runner.trace; - for (const spec of specs) { - const filepath = typeof spec === "string" ? spec : spec.filepath; - await $("collect_spec", { "code.file.path": filepath }, async () => { - var _runner$onCollectStar; - const testLocations = typeof spec === "string" ? undefined : spec.testLocations; - const file = createFileTask(filepath, config.root, config.name, runner.pool, runner.viteEnvironment); - setFileContext(file, Object.create(null)); - file.shuffle = config.sequence.shuffle; - (_runner$onCollectStar = runner.onCollectStart) === null || _runner$onCollectStar === void 0 ? void 0 : _runner$onCollectStar.call(runner, file); - clearCollectorContext(file, runner); - try { - var _runner$getImportDura; - 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 durations = (_runner$getImportDura = runner.getImportDurations) === null || _runner$getImportDura === void 0 ? void 0 : _runner$getImportDura.call(runner); - if (durations) { - file.importDurations = durations; - } - 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 === "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 { - // check that types are exhausted - c; - } - } - setHooks(file, fileHooks); - file.collectDuration = now$1() - collectStart; - } catch (e) { - var _runner$getImportDura2; - const error = processError(e); - file.result = { - state: "fail", - errors: [error] - }; - const durations = (_runner$getImportDura2 = runner.getImportDurations) === null || _runner$getImportDura2 === void 0 ? void 0 : _runner$getImportDura2.call(runner); - if (durations) { - file.importDurations = durations; - } - } - calculateSuiteHash(file); - const hasOnlyTasks = someTasksAreOnly(file); - interpretTaskModes(file, config.testNamePattern, testLocations, hasOnlyTasks, false, config.allowOnly); - if (file.mode === "queued") { - file.mode = "run"; - } - 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; -const { clearTimeout, setTimeout } = getSafeTimers(); -function updateSuiteHookState(task, name, state, runner) { - if (!task.result) { - task.result = { state: "run" }; - } - if (!task.result.hooks) { - task.result.hooks = {}; - } - const suiteHooks = task.result.hooks; - if (suiteHooks) { - suiteHooks[name] = state; - let event = state === "run" ? "before-hook-start" : "before-hook-end"; - if (name === "afterAll" || name === "afterEach") { - event = state === "run" ? "after-hook-start" : "after-hook-end"; - } - updateTask(event, task, 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, test, hooks, sequence) { - if (sequence === "stack") { - hooks = hooks.slice().reverse(); - } - if (!hooks.length) { - return; - } - const context = test.context; - const onTestFailed = test.context.onTestFailed; - const onTestFinished = test.context.onTestFinished; - context.onTestFailed = () => { - throw new Error(`Cannot call "onTestFailed" inside a test hook.`); - }; - context.onTestFinished = () => { - throw new Error(`Cannot call "onTestFinished" inside a test hook.`); - }; - if (sequence === "parallel") { - try { - await Promise.all(hooks.map((fn) => fn(test.context))); - } catch (e) { - failTask(test.result, e, runner.config.diffOptions); - } - } else { - for (const fn of hooks) { - try { - await fn(test.context); - } catch (e) { - failTask(test.result, e, runner.config.diffOptions); - } - } - } - context.onTestFailed = onTestFailed; - context.onTestFinished = onTestFinished; -} -async function callSuiteHook(suite, currentTask, name, runner, args) { - const sequence = runner.config.sequence.hooks; - const callbacks = []; - // stop at file level - const parentSuite = "filepath" in suite ? null : suite.suite || suite.file; - if (name === "beforeEach" && parentSuite) { - callbacks.push(...await callSuiteHook(parentSuite, currentTask, name, runner, args)); - } - const hooks = getSuiteHooks(suite, name, sequence); - if (hooks.length > 0) { - updateSuiteHookState(currentTask, name, "run", runner); - } - async function runHook(hook) { - return getBeforeHookCleanupCallback(hook, await hook(...args), name === "beforeEach" ? args[0] : undefined); - } - if (sequence === "parallel") { - callbacks.push(...await Promise.all(hooks.map((hook) => runHook(hook)))); - } else { - for (const hook of hooks) { - callbacks.push(await runHook(hook)); - } - } - if (hooks.length > 0) { - updateSuiteHookState(currentTask, name, "pass", runner); - } - if (name === "afterEach" && parentSuite) { - callbacks.push(...await callSuiteHook(parentSuite, currentTask, name, runner, args)); - } - return callbacks; -} -const packs = new Map(); -const eventsPacks = []; -const pendingTasksUpdates = []; -function sendTasksUpdate(runner) { - if (packs.size) { - var _runner$onTaskUpdate; - const taskPacks = Array.from(packs).map(([id, task]) => { - return [ - id, - task[0], - task[1] - ]; - }); - const p = (_runner$onTaskUpdate = runner.onTaskUpdate) === null || _runner$onTaskUpdate === void 0 ? void 0 : _runner$onTaskUpdate.call(runner, taskPacks, eventsPacks); - if (p) { - pendingTasksUpdates.push(p); - // remove successful promise to not grow array indefnitely, - // but keep rejections so finishSendTasksUpdate can handle them - p.then(() => pendingTasksUpdates.splice(pendingTasksUpdates.indexOf(p), 1), () => {}); - } - eventsPacks.length = 0; - packs.clear(); - } -} -async function finishSendTasksUpdate(runner) { - sendTasksUpdate(runner); - await Promise.all(pendingTasksUpdates); -} -function throttle(fn, ms) { - let last = 0; - let pendingCall; - return function call(...args) { - const now = unixNow(); - if (now - last > ms) { - last = now; - clearTimeout(pendingCall); - pendingCall = undefined; - return fn.apply(this, args); - } - // Make sure fn is still called even if there are no further calls - pendingCall ?? (pendingCall = setTimeout(() => call.bind(this)(...args), ms)); - }; -} -// throttle based on summary reporter's DURATION_UPDATE_INTERVAL_MS -const sendTasksUpdateThrottled = throttle(sendTasksUpdate, 100); -function updateTask(event, task, runner) { - eventsPacks.push([ - task.id, - event, - undefined - ]); - packs.set(task.id, [task.result, task.meta]); - sendTasksUpdateThrottled(runner); -} -async function callCleanupHooks(runner, cleanups) { - const sequence = runner.config.sequence.hooks; - if (sequence === "stack") { - cleanups = cleanups.slice().reverse(); - } - if (sequence === "parallel") { - await Promise.all(cleanups.map(async (fn) => { - if (typeof fn !== "function") { - return; - } - await fn(); - })); - } else { - for (const fn of cleanups) { - if (typeof fn !== "function") { - continue; - } - await fn(); - } - } -} -async function runTest(test, runner) { - var _runner$onBeforeRunTa, _test$result, _runner$onAfterRunTas; - await ((_runner$onBeforeRunTa = runner.onBeforeRunTask) === null || _runner$onBeforeRunTa === void 0 ? void 0 : _runner$onBeforeRunTa.call(runner, test)); - if (test.mode !== "run" && test.mode !== "queued") { - updateTask("test-prepare", test, runner); - updateTask("test-finished", test, runner); - return; - } - if (((_test$result = test.result) === null || _test$result === void 0 ? void 0 : _test$result.state) === "fail") { - // should not be possible to get here, I think this is just copy pasted from suite - // TODO: maybe someone fails tests in `beforeAll` hooks? - // https://github.com/vitest-dev/vitest/pull/7069 - updateTask("test-failed-early", test, runner); - return; - } - const start = now(); - test.result = { - state: "run", - startTime: unixNow(), - retryCount: 0 - }; - updateTask("test-prepare", test, runner); - const cleanupRunningTest = addRunningTest(test); - setCurrentTest(test); - const suite = test.suite || test.file; - const $ = runner.trace; - const repeats = test.repeats ?? 0; - for (let repeatCount = 0; repeatCount <= repeats; repeatCount++) { - const retry = test.retry ?? 0; - for (let retryCount = 0; retryCount <= retry; retryCount++) { - var _test$onFinished, _test$onFailed, _runner$onAfterRetryT, _test$result2, _test$result3; - let beforeEachCleanups = []; - try { - var _runner$onBeforeTryTa, _runner$onAfterTryTas; - await ((_runner$onBeforeTryTa = runner.onBeforeTryTask) === null || _runner$onBeforeTryTa === void 0 ? void 0 : _runner$onBeforeTryTa.call(runner, test, { - retry: retryCount, - repeats: repeatCount - })); - test.result.repeatCount = repeatCount; - beforeEachCleanups = await $("test.beforeEach", () => callSuiteHook(suite, test, "beforeEach", runner, [test.context, suite])); - if (runner.runTask) { - await $("test.callback", () => 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 $("test.callback", () => fn()); - } - await ((_runner$onAfterTryTas = runner.onAfterTryTask) === null || _runner$onAfterTryTas === void 0 ? void 0 : _runner$onAfterTryTas.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); - } - try { - var _runner$onTaskFinishe; - await ((_runner$onTaskFinishe = runner.onTaskFinished) === null || _runner$onTaskFinishe === void 0 ? void 0 : _runner$onTaskFinishe.call(runner, test)); - } catch (e) { - failTask(test.result, e, runner.config.diffOptions); - } - try { - await $("test.afterEach", () => callSuiteHook(suite, test, "afterEach", runner, [test.context, suite])); - if (beforeEachCleanups.length) { - await $("test.cleanup", () => callCleanupHooks(runner, beforeEachCleanups)); - } - await callFixtureCleanup(test.context); - } catch (e) { - failTask(test.result, e, runner.config.diffOptions); - } - if ((_test$onFinished = test.onFinished) === null || _test$onFinished === void 0 ? void 0 : _test$onFinished.length) { - await $("test.onFinished", () => callTestHooks(runner, test, test.onFinished, "stack")); - } - if (test.result.state === "fail" && ((_test$onFailed = test.onFailed) === null || _test$onFailed === void 0 ? void 0 : _test$onFailed.length)) { - await $("test.onFailed", () => callTestHooks(runner, test, test.onFailed, runner.config.sequence.hooks)); - } - test.onFailed = undefined; - test.onFinished = undefined; - await ((_runner$onAfterRetryT = runner.onAfterRetryTask) === null || _runner$onAfterRetryT === void 0 ? void 0 : _runner$onAfterRetryT.call(runner, test, { - retry: retryCount, - repeats: repeatCount - })); - // skipped with new PendingError - if (((_test$result2 = test.result) === null || _test$result2 === void 0 ? void 0 : _test$result2.pending) || ((_test$result3 = test.result) === null || _test$result3 === void 0 ? void 0 : _test$result3.state) === "skip") { - var _test$result4; - test.mode = "skip"; - test.result = { - state: "skip", - note: (_test$result4 = test.result) === null || _test$result4 === void 0 ? void 0 : _test$result4.note, - pending: true, - duration: now() - start - }; - updateTask("test-finished", test, runner); - setCurrentTest(undefined); - cleanupRunningTest(); - return; - } - if (test.result.state === "pass") { - break; - } - if (retryCount < retry) { - // reset state when retry test - test.result.state = "run"; - test.result.retryCount = (test.result.retryCount ?? 0) + 1; - } - // update retry info - updateTask("test-retried", test, runner); - } - } - // if test is marked to be failed, flip the result - 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 = undefined; - } - } - cleanupRunningTest(); - setCurrentTest(undefined); - test.result.duration = now() - start; - await ((_runner$onAfterRunTas = runner.onAfterRunTask) === null || _runner$onAfterRunTas === void 0 ? void 0 : _runner$onAfterRunTas.call(runner, test)); - updateTask("test-finished", test, runner); -} -function failTask(result, err, diffOptions) { - if (err instanceof PendingError) { - result.state = "skip"; - result.note = err.note; - result.pending = true; - 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("test-finished", t, runner); - if (t.type === "suite") { - markTasksAsSkipped(t, runner); - } - }); -} -async function runSuite(suite, runner) { - var _runner$onBeforeRunSu, _suite$result; - await ((_runner$onBeforeRunSu = runner.onBeforeRunSuite) === null || _runner$onBeforeRunSu === void 0 ? void 0 : _runner$onBeforeRunSu.call(runner, suite)); - if (((_suite$result = suite.result) === null || _suite$result === void 0 ? void 0 : _suite$result.state) === "fail") { - markTasksAsSkipped(suite, runner); - // failed during collection - updateTask("suite-failed-early", suite, runner); - return; - } - const start = now(); - const mode = suite.mode; - suite.result = { - state: mode === "skip" || mode === "todo" ? mode : "run", - startTime: unixNow() - }; - const $ = runner.trace; - updateTask("suite-prepare", suite, runner); - let beforeAllCleanups = []; - if (suite.mode === "skip") { - suite.result.state = "skip"; - updateTask("suite-finished", suite, runner); - } else if (suite.mode === "todo") { - suite.result.state = "todo"; - updateTask("suite-finished", suite, runner); - } else { - var _runner$onAfterRunSui; - try { - try { - beforeAllCleanups = await $("suite.beforeAll", () => 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 (suite.shuffle) { - // run describe block independently from tests - 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 $("suite.afterAll", () => callSuiteHook(suite, suite, "afterAll", runner, [suite])); - if (beforeAllCleanups.length) { - await $("suite.cleanup", () => callCleanupHooks(runner, beforeAllCleanups)); - } - if (suite.file === suite) { - const context = getFileContext(suite); - await callFixtureCleanup(context); - } - } catch (e) { - failTask(suite.result, e, runner.config.diffOptions); - } - if (suite.mode === "run" || suite.mode === "queued") { - if (!runner.config.passWithNoTests && !hasTests(suite)) { - var _suite$result$errors; - suite.result.state = "fail"; - if (!((_suite$result$errors = suite.result.errors) === null || _suite$result$errors === void 0 ? void 0 : _suite$result$errors.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"; - } - } - suite.result.duration = now() - start; - await ((_runner$onAfterRunSui = runner.onAfterRunSuite) === null || _runner$onAfterRunSui === void 0 ? void 0 : _runner$onAfterRunSui.call(runner, suite)); - updateTask("suite-finished", suite, runner); - } -} -let limitMaxConcurrency; -async function runSuiteChild(c, runner) { - const $ = runner.trace; - if (c.type === "test") { - return limitMaxConcurrency(() => { - var _c$location, _c$location2; - return $("run.test", { - "vitest.test.id": c.id, - "vitest.test.name": c.name, - "vitest.test.mode": c.mode, - "vitest.test.timeout": c.timeout, - "code.file.path": c.file.filepath, - "code.line.number": (_c$location = c.location) === null || _c$location === void 0 ? void 0 : _c$location.line, - "code.column.number": (_c$location2 = c.location) === null || _c$location2 === void 0 ? void 0 : _c$location2.column - }, () => runTest(c, runner)); - }); - } else if (c.type === "suite") { - var _c$location3, _c$location4; - return $("run.suite", { - "vitest.suite.id": c.id, - "vitest.suite.name": c.name, - "vitest.suite.mode": c.mode, - "code.file.path": c.file.filepath, - "code.line.number": (_c$location3 = c.location) === null || _c$location3 === void 0 ? void 0 : _c$location3.line, - "code.column.number": (_c$location4 = c.location) === null || _c$location4 === void 0 ? void 0 : _c$location4.column - }, () => runSuite(c, runner)); - } -} -async function runFiles(files, runner) { - limitMaxConcurrency ?? (limitMaxConcurrency = limitConcurrency(runner.config.maxConcurrency)); - for (const file of files) { - if (!file.tasks.length && !runner.config.passWithNoTests) { - var _file$result; - if (!((_file$result = file.result) === null || _file$result === void 0 || (_file$result = _file$result.errors) === null || _file$result === void 0 ? void 0 : _file$result.length)) { - const error = processError(new Error(`No test suite found in file ${file.filepath}`)); - file.result = { - state: "fail", - errors: [error] - }; - } - } - await runner.trace("run.spec", { - "code.file.path": file.filepath, - "vitest.suite.tasks.length": file.tasks.length - }, () => runSuite(file, runner)); - } -} -const workerRunners = new WeakSet(); -function defaultTrace(_, attributes, cb) { - if (typeof attributes === "function") { - return attributes(); - } - return cb(); -} -async function startTests(specs, runner) { - var _runner$cancel; - runner.trace ?? (runner.trace = defaultTrace); - const cancel = (_runner$cancel = runner.cancel) === null || _runner$cancel === void 0 ? void 0 : _runner$cancel.bind(runner); - // Ideally, we need to have an event listener for this, but only have a runner here. - // Adding another onCancel felt wrong (maybe it needs to be refactored) - runner.cancel = (reason) => { - // We intentionally create only one error since there is only one test run that can be cancelled - const error = new TestRunAbortError("The test run was aborted by the user.", reason); - getRunningTests().forEach((test) => abortContextSignal(test.context, error)); - return cancel === null || cancel === void 0 ? void 0 : cancel(reason); - }; - if (!workerRunners.has(runner)) { - var _runner$onCleanupWork; - (_runner$onCleanupWork = runner.onCleanupWorkerContext) === null || _runner$onCleanupWork === void 0 ? void 0 : _runner$onCleanupWork.call(runner, async () => { - var _runner$getWorkerCont; - const context = (_runner$getWorkerCont = runner.getWorkerContext) === null || _runner$getWorkerCont === void 0 ? void 0 : _runner$getWorkerCont.call(runner); - if (context) { - await callFixtureCleanup(context); - } - }); - workerRunners.add(runner); - } - try { - var _runner$onBeforeColle, _runner$onCollected, _runner$onBeforeRunFi, _runner$onAfterRunFil; - const paths = specs.map((f) => typeof f === "string" ? f : f.filepath); - await ((_runner$onBeforeColle = runner.onBeforeCollect) === null || _runner$onBeforeColle === void 0 ? void 0 : _runner$onBeforeColle.call(runner, paths)); - const files = await collectTests(specs, runner); - await ((_runner$onCollected = runner.onCollected) === null || _runner$onCollected === void 0 ? void 0 : _runner$onCollected.call(runner, files)); - await ((_runner$onBeforeRunFi = runner.onBeforeRunFiles) === null || _runner$onBeforeRunFi === void 0 ? void 0 : _runner$onBeforeRunFi.call(runner, files)); - await runFiles(files, runner); - await ((_runner$onAfterRunFil = runner.onAfterRunFiles) === null || _runner$onAfterRunFil === void 0 ? void 0 : _runner$onAfterRunFil.call(runner, files)); - await finishSendTasksUpdate(runner); - return files; - } finally { - runner.cancel = cancel; - } -} -async function publicCollect(specs, runner) { - var _runner$onBeforeColle2, _runner$onCollected2; - runner.trace ?? (runner.trace = defaultTrace); - const paths = specs.map((f) => typeof f === "string" ? f : f.filepath); - await ((_runner$onBeforeColle2 = runner.onBeforeCollect) === null || _runner$onBeforeColle2 === void 0 ? void 0 : _runner$onBeforeColle2.call(runner, paths)); - const files = await collectTests(specs, runner); - await ((_runner$onCollected2 = runner.onCollected) === null || _runner$onCollected2 === void 0 ? void 0 : _runner$onCollected2.call(runner, files)); - return files; -} - -/** -* @experimental -* @advanced -* -* Records a custom test artifact during test execution. -* -* This function allows you to attach structured data, files, or metadata to a test. -* -* Vitest automatically injects the source location where the artifact was created and manages any attachments you include. -* -* @param task - The test task context, typically accessed via `this.task` in custom matchers or `context.task` in tests -* @param artifact - The artifact to record. Must extend {@linkcode TestArtifactBase} -* -* @returns A promise that resolves to the recorded artifact with location injected -* -* @throws {Error} If called after the test has finished running -* @throws {Error} If the test runner doesn't support artifacts -* -* @example -* ```ts -* // In a custom assertion -* async function toHaveValidSchema(this: MatcherState, actual: unknown) { -* const validation = validateSchema(actual) -* -* await recordArtifact(this.task, { -* type: 'my-plugin:schema-validation', -* passed: validation.valid, -* errors: validation.errors, -* }) -* -* return { pass: validation.valid, message: () => '...' } -* } -* ``` -*/ -async function recordArtifact(task, artifact) { - const runner = getRunner(); - if (task.result && task.result.state !== "run") { - throw new Error(`Cannot record a test artifact outside of the test run. The test "${task.name}" finished running with the "${task.result.state}" state already.`); - } - const stack = findTestFileStackTrace(task.file.filepath, new Error("STACK_TRACE").stack); - if (stack) { - artifact.location = { - file: stack.file, - line: stack.line, - column: stack.column - }; - if (artifact.type === "internal:annotation") { - artifact.annotation.location = artifact.location; - } - } - if (Array.isArray(artifact.attachments)) { - for (const attachment of artifact.attachments) { - manageArtifactAttachment(attachment); - } - } - // annotations won't resolve as artifacts for backwards compatibility until next major - if (artifact.type === "internal:annotation") { - return artifact; - } - if (!runner.onTestArtifactRecord) { - throw new Error(`Test runner doesn't support test artifacts.`); - } - await finishSendTasksUpdate(runner); - const resolvedArtifact = await runner.onTestArtifactRecord(task, artifact); - task.artifacts.push(resolvedArtifact); - return resolvedArtifact; -} -const table = []; -for (let i = 65; i < 91; i++) { - table.push(String.fromCharCode(i)); -} -for (let i = 97; i < 123; i++) { - table.push(String.fromCharCode(i)); -} -for (let i = 0; i < 10; i++) { - table.push(i.toString(10)); -} -table.push("+", "/"); -function encodeUint8Array(bytes) { - let base64 = ""; - const len = bytes.byteLength; - for (let i = 0; i < len; i += 3) { - if (len === i + 1) { - const a = (bytes[i] & 252) >> 2; - const b = (bytes[i] & 3) << 4; - base64 += table[a]; - base64 += table[b]; - base64 += "=="; - } else if (len === i + 2) { - const a = (bytes[i] & 252) >> 2; - const b = (bytes[i] & 3) << 4 | (bytes[i + 1] & 240) >> 4; - const c = (bytes[i + 1] & 15) << 2; - base64 += table[a]; - base64 += table[b]; - base64 += table[c]; - base64 += "="; - } else { - const a = (bytes[i] & 252) >> 2; - const b = (bytes[i] & 3) << 4 | (bytes[i + 1] & 240) >> 4; - const c = (bytes[i + 1] & 15) << 2 | (bytes[i + 2] & 192) >> 6; - const d = bytes[i + 2] & 63; - base64 += table[a]; - base64 += table[b]; - base64 += table[c]; - base64 += table[d]; - } - } - return base64; -} -/** -* Records an async operation associated with a test task. -* -* This function tracks promises that should be awaited before a test completes. -* The promise is automatically removed from the test's promise list once it settles. -*/ -function recordAsyncOperation(test, promise) { - // if promise is explicitly awaited, remove it from the list - promise = promise.finally(() => { - if (!test.promises) { - return; - } - const index = test.promises.indexOf(promise); - if (index !== -1) { - test.promises.splice(index, 1); - } - }); - // record promise - if (!test.promises) { - test.promises = []; - } - test.promises.push(promise); - return promise; -} -/** -* Validates and prepares a test attachment for serialization. -* -* This function ensures attachments have either `body` or `path` set (but not both), and converts `Uint8Array` bodies to base64-encoded strings for easier serialization. -* -* @param attachment - The attachment to validate and prepare -* -* @throws {TypeError} If neither `body` nor `path` is provided -* @throws {TypeError} If both `body` and `path` are provided -*/ -function manageArtifactAttachment(attachment) { - if (attachment.body == null && !attachment.path) { - throw new TypeError(`Test attachment requires "body" or "path" to be set. Both are missing.`); - } - if (attachment.body && attachment.path) { - throw new TypeError(`Test attachment requires only one of "body" or "path" to be set. Both are specified.`); - } - // convert to a string so it's easier to serialise - if (attachment.body instanceof Uint8Array) { - attachment.body = encodeUint8Array(attachment.body); - } -} - -export { afterAll, afterEach, beforeAll, beforeEach, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, recordArtifact, setFn, setHooks, startTests, suite, test, updateTask }; |
