From f1ef2f6362d1607953d383e395d503feb5368613 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 15 Jul 2024 17:43:05 +0200 Subject: [PATCH] refactor(runner): add docs to @vitest/runner, remove barrel files (#6126) --- packages/pretty-format/src/index.ts | 11 +- packages/pretty-format/tsconfig.json | 4 +- packages/runner/rollup.config.js | 2 +- packages/runner/src/collect.ts | 2 +- packages/runner/src/context.ts | 6 +- packages/runner/src/errors.ts | 2 +- packages/runner/src/fixture.ts | 10 +- packages/runner/src/hooks.ts | 149 +++++++++++++++--- packages/runner/src/index.ts | 3 +- packages/runner/src/map.ts | 8 +- packages/runner/src/run.ts | 21 ++- packages/runner/src/setup.ts | 4 +- packages/runner/src/suite.ts | 134 ++++++++++++++-- packages/runner/src/test-state.ts | 4 +- packages/runner/src/types.ts | 52 ++++++ packages/runner/src/types/index.ts | 2 - packages/runner/src/types/tasks.ts | 42 +++-- packages/runner/src/utils/collect.ts | 8 +- packages/runner/src/utils/index.ts | 24 ++- .../runner/src/utils/limit-concurrency.ts | 2 +- packages/runner/src/utils/suite.ts | 4 +- packages/runner/src/utils/tasks.ts | 4 +- packages/runner/tsconfig.json | 6 + packages/utils/src/ast/esmWalker.ts | 6 +- packages/utils/src/ast/index.ts | 6 +- packages/utils/src/base.ts | 22 --- packages/utils/src/constants.ts | 2 - packages/utils/src/diff/cleanupSemantic.ts | 2 +- packages/utils/src/diff/constants.ts | 4 +- packages/utils/src/display.ts | 58 ++++++- packages/utils/src/error.ts | 18 ++- packages/utils/src/helpers.ts | 38 ++++- packages/utils/src/highlight.ts | 2 +- packages/utils/src/index.ts | 60 +++++-- packages/utils/src/offset.ts | 2 +- packages/utils/src/random.ts | 2 +- packages/utils/src/source-map.ts | 2 +- packages/utils/src/stringify.ts | 56 ------- packages/utils/src/timers.ts | 16 +- packages/utils/tsconfig.json | 4 + packages/vitest/src/node/reporters/base.ts | 5 +- 41 files changed, 593 insertions(+), 216 deletions(-) create mode 100644 packages/runner/src/types.ts delete mode 100644 packages/runner/src/types/index.ts delete mode 100644 packages/utils/src/base.ts delete mode 100644 packages/utils/src/constants.ts delete mode 100644 packages/utils/src/stringify.ts diff --git a/packages/pretty-format/src/index.ts b/packages/pretty-format/src/index.ts index 875bc00d7..3872d6844 100644 --- a/packages/pretty-format/src/index.ts +++ b/packages/pretty-format/src/index.ts @@ -402,7 +402,7 @@ const DEFAULT_THEME_KEYS = Object.keys(DEFAULT_THEME) as Array< keyof typeof DEFAULT_THEME > -export const DEFAULT_OPTIONS = { +export const DEFAULT_OPTIONS: Options = { callToJSON: true, compareKeys: undefined, escapeRegex: false, @@ -528,7 +528,14 @@ export function format(val: unknown, options?: OptionsReceived): string { return printComplexValue(val, getConfig(options), '', 0, []) } -export const plugins = { +export const plugins: { + AsymmetricMatcher: NewPlugin + DOMCollection: NewPlugin + DOMElement: NewPlugin + Immutable: NewPlugin + ReactElement: NewPlugin + ReactTestComponent: NewPlugin +} = { AsymmetricMatcher, DOMCollection, DOMElement, diff --git a/packages/pretty-format/tsconfig.json b/packages/pretty-format/tsconfig.json index 42a2e6729..0cf27b66f 100644 --- a/packages/pretty-format/tsconfig.json +++ b/packages/pretty-format/tsconfig.json @@ -1,7 +1,9 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "lib": ["ESNext", "DOM", "DOM.Iterable"] + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "moduleResolution": "Bundler", + "isolatedDeclarations": true }, "include": ["src/**/*"], "exclude": ["**/dist/**"] diff --git a/packages/runner/rollup.config.js b/packages/runner/rollup.config.js index 5b5fb6e1d..98b094206 100644 --- a/packages/runner/rollup.config.js +++ b/packages/runner/rollup.config.js @@ -17,7 +17,7 @@ const external = [ const entries = { index: 'src/index.ts', utils: 'src/utils/index.ts', - types: 'src/types/index.ts', + types: 'src/types.ts', } const plugins = [ diff --git a/packages/runner/src/collect.ts b/packages/runner/src/collect.ts index 63837a6ba..74c2d1230 100644 --- a/packages/runner/src/collect.ts +++ b/packages/runner/src/collect.ts @@ -1,5 +1,5 @@ import { processError } from '@vitest/utils/error' -import type { File, SuiteHooks } from './types' +import type { File, SuiteHooks } from './types/tasks' import type { VitestRunner } from './types/runner' import { calculateSuiteHash, diff --git a/packages/runner/src/context.ts b/packages/runner/src/context.ts index a4b77e38c..f03b389c2 100644 --- a/packages/runner/src/context.ts +++ b/packages/runner/src/context.ts @@ -7,7 +7,7 @@ import type { SuiteCollector, TaskContext, Test, -} from './types' +} from './types/tasks' import type { VitestRunner } from './types/runner' import { PendingError } from './errors' @@ -16,14 +16,14 @@ export const collectorContext: RuntimeContext = { currentSuite: null, } -export function collectTask(task: SuiteCollector) { +export function collectTask(task: SuiteCollector): void { collectorContext.currentSuite?.tasks.push(task) } export async function runWithSuite( suite: SuiteCollector, fn: () => Awaitable, -) { +): Promise { const prev = collectorContext.currentSuite collectorContext.currentSuite = suite await fn() diff --git a/packages/runner/src/errors.ts b/packages/runner/src/errors.ts index b9c50bcd4..3ce264122 100644 --- a/packages/runner/src/errors.ts +++ b/packages/runner/src/errors.ts @@ -1,4 +1,4 @@ -import type { TaskBase } from './types' +import type { TaskBase } from './types/tasks' export class PendingError extends Error { public code = 'VITEST_PENDING' diff --git a/packages/runner/src/fixture.ts b/packages/runner/src/fixture.ts index 79f4a5d89..11bf8756b 100644 --- a/packages/runner/src/fixture.ts +++ b/packages/runner/src/fixture.ts @@ -1,6 +1,6 @@ import { createDefer, isObject } from '@vitest/utils' import { getFixture } from './map' -import type { FixtureOptions, TestContext } from './types' +import type { FixtureOptions, TestContext } from './types/tasks' export interface FixtureItem extends FixtureOptions { prop: string @@ -18,7 +18,9 @@ export interface FixtureItem extends FixtureOptions { export function mergeContextFixtures( fixtures: Record, context: { fixtures?: FixtureItem[] } = {}, -) { +): { + fixtures?: FixtureItem[] + } { const fixtureOptionKeys = ['auto'] const fixtureArray: FixtureItem[] = Object.entries(fixtures).map( ([prop, value]) => { @@ -69,7 +71,7 @@ const cleanupFnArrayMap = new Map< Array<() => void | Promise> >() -export async function callFixtureCleanup(context: TestContext) { +export async function callFixtureCleanup(context: TestContext): Promise { const cleanupFnArray = cleanupFnArrayMap.get(context) ?? [] for (const cleanup of cleanupFnArray.reverse()) { await cleanup() @@ -78,7 +80,7 @@ export async function callFixtureCleanup(context: TestContext) { } export function withFixtures(fn: Function, testContext?: TestContext) { - return (hookContext?: TestContext) => { + return (hookContext?: TestContext): any => { const context: (TestContext & { [key: string]: any }) | undefined = hookContext || testContext diff --git a/packages/runner/src/hooks.ts b/packages/runner/src/hooks.ts index 758a6fc04..44ea8eea2 100644 --- a/packages/runner/src/hooks.ts +++ b/packages/runner/src/hooks.ts @@ -1,9 +1,13 @@ import type { + AfterAllListener, + AfterEachListener, + BeforeAllListener, + BeforeEachListener, OnTestFailedHandler, OnTestFinishedHandler, - SuiteHooks, + TaskHook, TaskPopulated, -} from './types' +} from './types/tasks' import { getCurrentSuite, getRunner } from './suite' import { getCurrentTest } from './test-state' import { withTimeout } from './context' @@ -13,65 +17,172 @@ function getDefaultHookTimeout() { return getRunner().config.hookTimeout } -// suite hooks -export function beforeAll(fn: SuiteHooks['beforeAll'][0], timeout?: number) { +/** + * 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 + * // Example of using beforeAll to set up a database connection + * beforeAll(async () => { + * await database.connect(); + * }); + */ +export function beforeAll(fn: BeforeAllListener, timeout?: number): void { return getCurrentSuite().on( 'beforeAll', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true), ) } -export function afterAll(fn: SuiteHooks['afterAll'][0], timeout?: number) { + +/** + * 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 + * // Example of using afterAll to close a database connection + * afterAll(async () => { + * await database.disconnect(); + * }); + */ +export function afterAll(fn: AfterAllListener, timeout?: number): void { return getCurrentSuite().on( 'afterAll', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true), ) } + +/** + * 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 + * // Example of using beforeEach to reset a database state + * beforeEach(async () => { + * await database.reset(); + * }); + */ export function beforeEach( - fn: SuiteHooks['beforeEach'][0], + fn: BeforeEachListener, timeout?: number, -) { +): void { return getCurrentSuite().on( 'beforeEach', withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true), ) } + +/** + * 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 + * // Example of using afterEach to delete temporary files created during a test + * afterEach(async () => { + * await fileSystem.deleteTempFiles(); + * }); + */ export function afterEach( - fn: SuiteHooks['afterEach'][0], + fn: AfterEachListener, timeout?: number, -) { +): void { return getCurrentSuite().on( 'afterEach', withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true), ) } -export const onTestFailed = createTestHook( +/** + * 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 + * // Example of using onTestFailed to log failure details + * onTestFailed(({ errors }) => { + * console.log(`Test failed: ${test.name}`, errors); + * }); + */ +export const onTestFailed: TaskHook = createTestHook( 'onTestFailed', - (test, handler) => { + (test, handler, timeout) => { test.onFailed ||= [] - test.onFailed.push(handler) + test.onFailed.push( + withTimeout(handler, timeout ?? getDefaultHookTimeout(), true), + ) }, ) -export const onTestFinished = createTestHook( +/** + * 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. + * + * @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 + * // Example of using onTestFinished for cleanup + * const db = await connectToDatabase(); + * onTestFinished(async () => { + * await db.disconnect(); + * }); + */ +export const onTestFinished: TaskHook = createTestHook( 'onTestFinished', - (test, handler) => { + (test, handler, timeout) => { test.onFinished ||= [] - test.onFinished.push(handler) + test.onFinished.push( + withTimeout(handler, timeout ?? getDefaultHookTimeout(), true), + ) }, ) function createTestHook( name: string, - handler: (test: TaskPopulated, handler: T) => void, -) { - return (fn: T) => { + handler: (test: TaskPopulated, handler: T, timeout?: number) => void, +): TaskHook { + return (fn: T, timeout?: number) => { const current = getCurrentTest() if (!current) { throw new Error(`Hook ${name}() can only be called inside a test`) } - return handler(current, fn) + return handler(current, fn, timeout) } } diff --git a/packages/runner/src/index.ts b/packages/runner/src/index.ts index 56c5cc592..906c387ca 100644 --- a/packages/runner/src/index.ts +++ b/packages/runner/src/index.ts @@ -18,4 +18,5 @@ export { export { setFn, getFn, getHooks, setHooks } from './map' export { getCurrentTest } from './test-state' export { processError } from '@vitest/utils/error' -export * from './types' + +export type * from './types' diff --git a/packages/runner/src/map.ts b/packages/runner/src/map.ts index 91964b16f..4450ca59e 100644 --- a/packages/runner/src/map.ts +++ b/packages/runner/src/map.ts @@ -1,5 +1,5 @@ import type { Awaitable } from '@vitest/utils' -import type { Custom, Suite, SuiteHooks, Test, TestContext } from './types' +import type { Custom, Suite, SuiteHooks, Test, TestContext } from './types/tasks' import type { FixtureItem } from './fixture' // use WeakMap here to make the Test and Suite object serializable @@ -7,7 +7,7 @@ const fnMap = new WeakMap() const fixtureMap = new WeakMap() const hooksMap = new WeakMap() -export function setFn(key: Test | Custom, fn: () => Awaitable) { +export function setFn(key: Test | Custom, fn: () => Awaitable): void { fnMap.set(key, fn) } @@ -18,7 +18,7 @@ export function getFn(key: Task): () => Awaitable { export function setFixture( key: TestContext, fixture: FixtureItem[] | undefined, -) { +): void { fixtureMap.set(key, fixture) } @@ -26,7 +26,7 @@ export function getFixture(key: Context): FixtureItem[] { return fixtureMap.get(key as any) } -export function setHooks(key: Suite, hooks: SuiteHooks) { +export function setHooks(key: Suite, hooks: SuiteHooks): void { hooksMap.set(key, hooks) } diff --git a/packages/runner/src/run.ts b/packages/runner/src/run.ts index 68b6623c8..d27ee69d5 100644 --- a/packages/runner/src/run.ts +++ b/packages/runner/src/run.ts @@ -17,7 +17,7 @@ import type { TaskResultPack, TaskState, Test, -} from './types' +} from './types/tasks' import { partitionSuiteChildren } from './utils/suite' import { limitConcurrency } from './utils/limit-concurrency' import { getFn, getHooks } from './map' @@ -90,8 +90,7 @@ export async function callSuiteHook( const callbacks: HookCleanupCallback[] = [] // stop at file level - const parentSuite: Suite | null - = 'filepath' in suite ? null : suite.suite || suite.file + const parentSuite: Suite | null = 'filepath' in suite ? null : suite.suite || suite.file if (name === 'beforeEach' && parentSuite) { callbacks.push( @@ -105,12 +104,12 @@ export async function callSuiteHook( if (sequence === 'parallel') { callbacks.push( - ...(await Promise.all(hooks.map(fn => fn(...(args as any))))), + ...(await Promise.all(hooks.map(hook => (hook as any)(...args)))), ) } else { for (const hook of hooks) { - callbacks.push(await hook(...(args as any))) + callbacks.push(await (hook as any)(...args)) } } @@ -129,7 +128,7 @@ const packs = new Map() let updateTimer: any let previousUpdate: Promise | undefined -export function updateTask(task: Task, runner: VitestRunner) { +export function updateTask(task: Task, runner: VitestRunner): void { packs.set(task.id, [task.result, task.meta]) const { clearTimeout, setTimeout } = getSafeTimers() @@ -166,7 +165,7 @@ async function callCleanupHooks(cleanups: HookCleanupCallback[]) { ) } -export async function runTest(test: Test | Custom, runner: VitestRunner) { +export async function runTest(test: Test | Custom, runner: VitestRunner): Promise { await runner.onBeforeRunTask?.(test) if (test.mode !== 'run') { @@ -364,7 +363,7 @@ function markTasksAsSkipped(suite: Suite, runner: VitestRunner) { }) } -export async function runSuite(suite: Suite, runner: VitestRunner) { +export async function runSuite(suite: Suite, runner: VitestRunner): Promise { await runner.onBeforeRunSuite?.(suite) if (suite.result?.state === 'fail') { @@ -477,7 +476,7 @@ async function runSuiteChild(c: Task, runner: VitestRunner) { } } -export async function runFiles(files: File[], runner: VitestRunner) { +export async function runFiles(files: File[], runner: VitestRunner): Promise { limitMaxConcurrency ??= limitConcurrency(runner.config.maxConcurrency) for (const file of files) { @@ -496,7 +495,7 @@ export async function runFiles(files: File[], runner: VitestRunner) { } } -export async function startTests(paths: string[], runner: VitestRunner) { +export async function startTests(paths: string[], runner: VitestRunner): Promise { await runner.onBeforeCollect?.(paths) const files = await collectTests(paths, runner) @@ -513,7 +512,7 @@ export async function startTests(paths: string[], runner: VitestRunner) { return files } -async function publicCollect(paths: string[], runner: VitestRunner) { +async function publicCollect(paths: string[], runner: VitestRunner): Promise { await runner.onBeforeCollect?.(paths) const files = await collectTests(paths, runner) diff --git a/packages/runner/src/setup.ts b/packages/runner/src/setup.ts index f54a8b033..ea3a12926 100644 --- a/packages/runner/src/setup.ts +++ b/packages/runner/src/setup.ts @@ -1,10 +1,10 @@ import { toArray } from '@vitest/utils' -import type { VitestRunner, VitestRunnerConfig } from './types' +import type { VitestRunner, VitestRunnerConfig } from './types/runner' export async function runSetupFiles( config: VitestRunnerConfig, runner: VitestRunner, -) { +): Promise { const files = toArray(config.setupFiles) if (config.sequence.setupFiles === 'parallel') { await Promise.all( diff --git a/packages/runner/src/suite.ts b/packages/runner/src/suite.ts index 9976c2293..93c6cf4bd 100644 --- a/packages/runner/src/suite.ts +++ b/packages/runner/src/suite.ts @@ -24,7 +24,7 @@ import type { TestAPI, TestFunction, TestOptions, -} from './types' +} from './types/tasks' import type { VitestRunner } from './types/runner' import { createChainable } from './utils/chain' import { @@ -39,9 +39,63 @@ import type { FixtureItem } from './fixture' import { mergeContextFixtures, withFixtures } from './fixture' import { getCurrentTest } from './test-state' -// apis -export const suite = createSuite() -export const test = createTest(function ( +/** + * 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 + * // 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 + * // 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'); + * }); + * }); + * }); + */ +export const suite: SuiteAPI = 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 + * // Define a simple test + * test('should add two numbers', () => { + * expect(add(1, 2)).toBe(3); + * }); + * + * @example + * // Define a test with options + * test('should subtract two numbers', { retry: 3 }, () => { + * expect(subtract(5, 2)).toBe(3); + * }); + */ +export const test: TestAPI = createTest(function ( name: string | Function, optionsOrFn?: TestOptions | TestFunction, optionsOrTest?: number | TestOptions | TestFunction, @@ -60,23 +114,77 @@ export const test = createTest(function ( ) }) -// alias -export const describe = suite -export const it = test +/** + * 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 + * // 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 + * // 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'); + * }); + * }); + * }); + */ +export const describe: SuiteAPI = 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 + * // Define a simple test + * it('adds two numbers', () => { + * expect(add(1, 2)).toBe(3); + * }); + * + * @example + * // Define a test with options + * it('subtracts two numbers', { retry: 3 }, () => { + * expect(subtract(5, 2)).toBe(3); + * }); + */ +export const it: TestAPI = test let runner: VitestRunner let defaultSuite: SuiteCollector let currentTestFilepath: string -export function getDefaultSuite() { +export function getDefaultSuite(): SuiteCollector { return defaultSuite } -export function getTestFilepath() { +export function getTestFilepath(): string { return currentTestFilepath } -export function getRunner() { +export function getRunner(): VitestRunner { return runner } @@ -89,7 +197,7 @@ function createDefaultSuite(runner: VitestRunner) { export function clearCollectorContext( filepath: string, currentRunner: VitestRunner, -) { +): void { if (!defaultSuite) { defaultSuite = createDefaultSuite(currentRunner) } @@ -105,7 +213,7 @@ export function getCurrentSuite() { || defaultSuite) as SuiteCollector } -export function createSuiteHooks() { +export function createSuiteHooks(): SuiteHooks { return { beforeAll: [], afterAll: [], @@ -468,7 +576,7 @@ function createSuite() { export function createTaskCollector( fn: (...args: any[]) => any, context?: Record, -) { +): CustomAPI { const taskFn = fn as any taskFn.each = function ( diff --git a/packages/runner/src/test-state.ts b/packages/runner/src/test-state.ts index a661eea3a..6e7bd0195 100644 --- a/packages/runner/src/test-state.ts +++ b/packages/runner/src/test-state.ts @@ -1,8 +1,8 @@ -import type { Custom, Test } from './types' +import type { Custom, Test } from './types/tasks.ts' let _test: Test | Custom | undefined -export function setCurrentTest(test: T | undefined) { +export function setCurrentTest(test: T | undefined): void { _test = test } diff --git a/packages/runner/src/types.ts b/packages/runner/src/types.ts new file mode 100644 index 000000000..cfd7c420c --- /dev/null +++ b/packages/runner/src/types.ts @@ -0,0 +1,52 @@ +export type { + RunMode, + TaskState, + TaskBase, + TaskPopulated, + TaskMeta, + TaskResult, + TaskResultPack, + Suite, + File, + Test, + Custom, + Task, + DoneCallback, + TestFunction, + TestOptions, + CustomAPI, + TestAPI, + FixtureOptions, + Use, + FixtureFn, + Fixture, + Fixtures, + InferFixturesTypes, + SuiteAPI, + HookListener, + HookCleanupCallback, + SuiteHooks, + TaskCustomOptions, + SuiteCollector, + SuiteFactory, + RuntimeContext, + TestContext, + TaskContext, + ExtendedContext, + OnTestFailedHandler, + OnTestFinishedHandler, + SequenceHooks, + SequenceSetupFiles, + AfterAllListener, + AfterEachListener, + BeforeAllListener, + BeforeEachListener, + TaskHook, +} from './types/tasks' +export type { + VitestRunnerConfig, + VitestRunnerImportSource, + VitestRunnerConstructor, + CancelReason, + VitestRunner, +} from './types/runner' diff --git a/packages/runner/src/types/index.ts b/packages/runner/src/types/index.ts deleted file mode 100644 index 01ac112a8..000000000 --- a/packages/runner/src/types/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './tasks' -export * from './runner' diff --git a/packages/runner/src/types/tasks.ts b/packages/runner/src/types/tasks.ts index 5ada3ec47..0ce647420 100644 --- a/packages/runner/src/types/tasks.ts +++ b/packages/runner/src/types/tasks.ts @@ -360,22 +360,42 @@ export type SuiteAPI = ChainableSuiteAPI & runIf: (condition: any) => ChainableSuiteAPI } +/** + * @deprecated + */ export type HookListener = ( ...args: T ) => Awaitable export type HookCleanupCallback = (() => Awaitable) | void +export interface BeforeAllListener { + (suite: Readonly): Awaitable +} + +export interface AfterAllListener { + (suite: Readonly): Awaitable +} + +export interface BeforeEachListener { + ( + context: ExtendedContext & ExtraContext, + suite: Readonly + ): Awaitable +} + +export interface AfterEachListener { + ( + context: ExtendedContext & ExtraContext, + suite: Readonly + ): Awaitable +} + export interface SuiteHooks { - beforeAll: HookListener<[Readonly], HookCleanupCallback>[] - afterAll: HookListener<[Readonly]>[] - beforeEach: HookListener< - [ExtendedContext & ExtraContext, Readonly], - HookCleanupCallback - >[] - afterEach: HookListener< - [ExtendedContext & ExtraContext, Readonly] - >[] + beforeAll: BeforeAllListener[] + afterAll: AfterAllListener[] + beforeEach: BeforeEachListener[] + afterEach: AfterEachListener[] } export interface TaskCustomOptions extends TestOptions { @@ -451,5 +471,9 @@ export type ExtendedContext = TaskContext & export type OnTestFailedHandler = (result: TaskResult) => Awaitable export type OnTestFinishedHandler = (result: TaskResult) => Awaitable +export interface TaskHook { + (fn: HookListener, timeout?: number): void +} + export type SequenceHooks = 'stack' | 'list' | 'parallel' export type SequenceSetupFiles = 'list' | 'parallel' diff --git a/packages/runner/src/utils/collect.ts b/packages/runner/src/utils/collect.ts index 7173a048a..376994ca9 100644 --- a/packages/runner/src/utils/collect.ts +++ b/packages/runner/src/utils/collect.ts @@ -1,6 +1,6 @@ import { processError } from '@vitest/utils/error' import { relative } from 'pathe' -import type { File, Suite, TaskBase } from '../types' +import type { File, Suite, TaskBase } from '../types/tasks' /** * If any tasks been marked as `only`, mark all other tasks as `skip`. @@ -11,7 +11,7 @@ export function interpretTaskModes( onlyMode?: boolean, parentIsOnly?: boolean, allowOnly?: boolean, -) { +): void { const suiteIsOnly = parentIsOnly || suite.mode === 'only' suite.tasks.forEach((t) => { @@ -105,7 +105,7 @@ export function generateHash(str: string): string { return `${hash}` } -export function calculateSuiteHash(parent: Suite) { +export function calculateSuiteHash(parent: Suite): void { parent.tasks.forEach((t, idx) => { t.id = `${parent.id}_${idx}` if (t.type === 'suite') { @@ -119,7 +119,7 @@ export function createFileTask( root: string, projectName: string, pool?: string, -) { +): File { const path = relative(root, filepath) const file: File = { id: generateHash(`${path}${projectName || ''}`), diff --git a/packages/runner/src/utils/index.ts b/packages/runner/src/utils/index.ts index 83c5b1633..1e1cf3ae9 100644 --- a/packages/runner/src/utils/index.ts +++ b/packages/runner/src/utils/index.ts @@ -1,5 +1,19 @@ -export * from './collect' -export * from './suite' -export * from './tasks' -export * from './chain' -export * from './limit-concurrency' +export { + interpretTaskModes, + someTasksAreOnly, + generateHash, + calculateSuiteHash, + createFileTask, +} from './collect' +export { partitionSuiteChildren } from './suite' +export { + isAtomTest, + getTests, + getTasks, + getSuites, + hasTests, + hasFailed, + getNames, +} from './tasks' +export { createChainable, type ChainableFunction } from './chain' +export { limitConcurrency } from './limit-concurrency' diff --git a/packages/runner/src/utils/limit-concurrency.ts b/packages/runner/src/utils/limit-concurrency.ts index 3ba509da2..4f42d5792 100644 --- a/packages/runner/src/utils/limit-concurrency.ts +++ b/packages/runner/src/utils/limit-concurrency.ts @@ -4,7 +4,7 @@ type QueueNode = [value: T, next?: QueueNode] /** * Return a function for running multiple async operations with limited concurrency. */ -export function limitConcurrency(concurrency = Infinity): (func: (...args: Args) => PromiseLike | T, ...args: Args) => Promise { +export function limitConcurrency(concurrency: number = Infinity): (func: (...args: Args) => PromiseLike | T, ...args: Args) => Promise { // The number of currently active + pending tasks. let count = 0 diff --git a/packages/runner/src/utils/suite.ts b/packages/runner/src/utils/suite.ts index 1c9d89aad..b0eb1f9d4 100644 --- a/packages/runner/src/utils/suite.ts +++ b/packages/runner/src/utils/suite.ts @@ -1,9 +1,9 @@ -import type { Suite, Task } from '../types' +import type { Suite, Task } from '../types/tasks' /** * Partition in tasks groups by consecutive concurrent */ -export function partitionSuiteChildren(suite: Suite) { +export function partitionSuiteChildren(suite: Suite): Task[][] { let tasksGroup: Task[] = [] const tasksGroups: Task[][] = [] for (const c of suite.tasks) { diff --git a/packages/runner/src/utils/tasks.ts b/packages/runner/src/utils/tasks.ts index 50ac4cedc..3212de13c 100644 --- a/packages/runner/src/utils/tasks.ts +++ b/packages/runner/src/utils/tasks.ts @@ -1,5 +1,5 @@ import { type Arrayable, toArray } from '@vitest/utils' -import type { Custom, Suite, Task, Test } from '../types' +import type { Custom, Suite, Task, Test } from '../types/tasks' export function isAtomTest(s: Task): s is Test | Custom { return s.type === 'test' || s.type === 'custom' @@ -54,7 +54,7 @@ export function hasFailed(suite: Arrayable): boolean { ) } -export function getNames(task: Task) { +export function getNames(task: Task): string[] { const names = [task.name] let current: Task | undefined = task diff --git a/packages/runner/tsconfig.json b/packages/runner/tsconfig.json index 8bcb003f8..1f52dab27 100644 --- a/packages/runner/tsconfig.json +++ b/packages/runner/tsconfig.json @@ -1,5 +1,11 @@ { "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "isolatedDeclarations": true + }, "include": ["./src/**/*.ts"], "exclude": ["./dist"] } diff --git a/packages/utils/src/ast/esmWalker.ts b/packages/utils/src/ast/esmWalker.ts index ab534ec73..c233112a5 100644 --- a/packages/utils/src/ast/esmWalker.ts +++ b/packages/utils/src/ast/esmWalker.ts @@ -27,7 +27,7 @@ interface Visitors { } const isNodeInPatternWeakSet = new WeakSet<_Node>() -export function setIsNodeInPattern(node: Property) { +export function setIsNodeInPattern(node: Property): WeakSet<_Node> { return isNodeInPatternWeakSet.add(node) } export function isNodeInPattern(node: _Node): node is Property { @@ -41,7 +41,7 @@ export function isNodeInPattern(node: _Node): node is Property { export function esmWalker( root: Node, { onIdentifier, onImportMeta, onDynamicImport }: Visitors, -) { +): void { const parentStack: Node[] = [] const varKindStack: VariableDeclaration['kind'][] = [] const scopeMap = new WeakMap<_Node, Set>() @@ -292,7 +292,7 @@ export function isStaticProperty(node: _Node): node is Property { return node && node.type === 'Property' && !node.computed } -export function isStaticPropertyKey(node: _Node, parent: _Node) { +export function isStaticPropertyKey(node: _Node, parent: _Node): boolean { return isStaticProperty(parent) && parent.key === node } diff --git a/packages/utils/src/ast/index.ts b/packages/utils/src/ast/index.ts index bf98e57a9..370a401cc 100644 --- a/packages/utils/src/ast/index.ts +++ b/packages/utils/src/ast/index.ts @@ -47,7 +47,7 @@ interface Visitors { } const isNodeInPatternWeakSet = new WeakSet<_Node>() -export function setIsNodeInPattern(node: Property) { +export function setIsNodeInPattern(node: Property): WeakSet<_Node> { return isNodeInPatternWeakSet.add(node) } export function isNodeInPattern(node: _Node): node is Property { @@ -61,7 +61,7 @@ export function isNodeInPattern(node: _Node): node is Property { export function esmWalker( root: Node, { onIdentifier, onImportMeta, onDynamicImport, onCallExpression }: Visitors, -) { +): void { const parentStack: Node[] = [] const varKindStack: VariableDeclaration['kind'][] = [] const scopeMap = new WeakMap<_Node, Set>() @@ -340,7 +340,7 @@ export function isStaticProperty(node: _Node): node is Property { return node && node.type === 'Property' && !node.computed } -export function isStaticPropertyKey(node: _Node, parent: _Node) { +export function isStaticPropertyKey(node: _Node, parent: _Node): boolean { return isStaticProperty(parent) && parent.key === node } diff --git a/packages/utils/src/base.ts b/packages/utils/src/base.ts deleted file mode 100644 index 892196d81..000000000 --- a/packages/utils/src/base.ts +++ /dev/null @@ -1,22 +0,0 @@ -interface ErrorOptions { - message?: string - stackTraceLimit?: number -} -/** - * Get original stacktrace without source map support the most performant way. - * - Create only 1 stack frame. - * - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms). - */ -export function createSimpleStackTrace(options?: ErrorOptions) { - const { message = '$$stack trace error', stackTraceLimit = 1 } - = options || {} - const limit = Error.stackTraceLimit - const prepareStackTrace = Error.prepareStackTrace - Error.stackTraceLimit = stackTraceLimit - Error.prepareStackTrace = e => e.stack - const err = new Error(message) - const stackTrace = err.stack || '' - Error.prepareStackTrace = prepareStackTrace - Error.stackTraceLimit = limit - return stackTrace -} diff --git a/packages/utils/src/constants.ts b/packages/utils/src/constants.ts deleted file mode 100644 index 1b285a267..000000000 --- a/packages/utils/src/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const SAFE_TIMERS_SYMBOL = Symbol('vitest:SAFE_TIMERS') -export const SAFE_COLORS_SYMBOL = Symbol('vitest:SAFE_COLORS') diff --git a/packages/utils/src/diff/cleanupSemantic.ts b/packages/utils/src/diff/cleanupSemantic.ts index 09e396e78..b794df613 100644 --- a/packages/utils/src/diff/cleanupSemantic.ts +++ b/packages/utils/src/diff/cleanupSemantic.ts @@ -189,7 +189,7 @@ const diff_commonOverlap_ = function (text1: string, text2: string): number { * Reduce the number of edits by eliminating semantically trivial equalities. * @param {!Array.} diffs Array of diff tuples. */ -const diff_cleanupSemantic = function (diffs: Array) { +const diff_cleanupSemantic = function (diffs: Array): void { let changes = false const equalities = [] // Stack of indices where equalities are found. let equalitiesLength = 0 // Keeping our own length var is faster in JS. diff --git a/packages/utils/src/diff/constants.ts b/packages/utils/src/diff/constants.ts index 4245a83c1..d67ba5881 100644 --- a/packages/utils/src/diff/constants.ts +++ b/packages/utils/src/diff/constants.ts @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -export const NO_DIFF_MESSAGE = 'Compared values have no visual difference.' +export const NO_DIFF_MESSAGE: string = 'Compared values have no visual difference.' -export const SIMILAR_MESSAGE +export const SIMILAR_MESSAGE: string = 'Compared values serialize to the same structure.\n' + 'Printing internal object structure without calling `toJSON` instead.' diff --git a/packages/utils/src/display.ts b/packages/utils/src/display.ts index eee5ac8c0..67c9902b1 100644 --- a/packages/utils/src/display.ts +++ b/packages/utils/src/display.ts @@ -1,5 +1,10 @@ // since this is already part of Vitest via Chai, we can just reuse it without increasing the size of bundle import * as loupe from 'loupe' +import type { PrettyFormatOptions } from '@vitest/pretty-format' +import { + format as prettyFormat, + plugins as prettyFormatPlugins, +} from '@vitest/pretty-format' type Inspect = (value: unknown, options: Options) => string interface Options { @@ -18,9 +23,60 @@ interface Options { type LoupeOptions = Partial +const { + AsymmetricMatcher, + DOMCollection, + DOMElement, + Immutable, + ReactElement, + ReactTestComponent, +} = prettyFormatPlugins + +const PLUGINS = [ + ReactTestComponent, + ReactElement, + DOMElement, + DOMCollection, + Immutable, + AsymmetricMatcher, +] + +export function stringify( + object: unknown, + maxDepth = 10, + { maxLength, ...options }: PrettyFormatOptions & { maxLength?: number } = {}, +): string { + const MAX_LENGTH = maxLength ?? 10000 + let result + + try { + result = prettyFormat(object, { + maxDepth, + escapeString: false, + // min: true, + plugins: PLUGINS, + ...options, + }) + } + catch { + result = prettyFormat(object, { + callToJSON: false, + maxDepth, + escapeString: false, + // min: true, + plugins: PLUGINS, + ...options, + }) + } + + return result.length >= MAX_LENGTH && maxDepth > 1 + ? stringify(object, Math.floor(maxDepth / 2)) + : result +} + const formatRegExp = /%[sdjifoOc%]/g -export function format(...args: unknown[]) { +export function format(...args: unknown[]): string { if (typeof args[0] !== 'string') { const objects = [] for (let i = 0; i < args.length; i++) { diff --git a/packages/utils/src/error.ts b/packages/utils/src/error.ts index e1dc8fbdb..040aef67d 100644 --- a/packages/utils/src/error.ts +++ b/packages/utils/src/error.ts @@ -1,7 +1,6 @@ import { type DiffOptions, diff } from './diff' -import { format } from './display' +import { format, stringify } from './display' import { deepClone, getOwnProperties, getType } from './helpers' -import { stringify } from './stringify' // utils is bundled for any environment and might not support `Element` declare class Element { @@ -28,7 +27,7 @@ function getUnserializableMessage(err: unknown) { } // https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm -export function serializeError(val: any, seen = new WeakMap()): any { +export function serializeError(val: any, seen: WeakMap = new WeakMap()): any { if (!val || typeof val === 'string') { return val } @@ -113,8 +112,8 @@ function normalizeErrorMessage(message: string) { export function processError( err: any, diffOptions?: DiffOptions, - seen = new WeakSet(), -) { + seen: WeakSet = new WeakSet(), +): any { if (!err || typeof err !== 'object') { return { message: err } } @@ -199,9 +198,12 @@ function isReplaceable(obj1: any, obj2: any) { export function replaceAsymmetricMatcher( actual: any, expected: any, - actualReplaced = new WeakSet(), - expectedReplaced = new WeakSet(), -) { + actualReplaced: WeakSet = new WeakSet(), + expectedReplaced: WeakSet = new WeakSet(), +): { + replacedActual: any + replacedExpected: any + } { if (!isReplaceable(actual, expected)) { return { replacedActual: actual, replacedExpected: expected } } diff --git a/packages/utils/src/helpers.ts b/packages/utils/src/helpers.ts index 19e3e63d0..dd7bc3c93 100644 --- a/packages/utils/src/helpers.ts +++ b/packages/utils/src/helpers.ts @@ -4,6 +4,30 @@ interface CloneOptions { forceWritable?: boolean } +interface ErrorOptions { + message?: string + stackTraceLimit?: number +} + +/** + * Get original stacktrace without source map support the most performant way. + * - Create only 1 stack frame. + * - Rewrite prepareStackTrace to bypass "support-stack-trace" (usually takes ~250ms). + */ +export function createSimpleStackTrace(options?: ErrorOptions): string { + const { message = '$$stack trace error', stackTraceLimit = 1 } + = options || {} + const limit = Error.stackTraceLimit + const prepareStackTrace = Error.prepareStackTrace + Error.stackTraceLimit = stackTraceLimit + Error.prepareStackTrace = e => e.stack + const err = new Error(message) + const stackTrace = err.stack || '' + Error.prepareStackTrace = prepareStackTrace + Error.stackTraceLimit = limit + return stackTrace +} + export function notNullish(v: T | null | undefined): v is NonNullable { return v != null } @@ -22,13 +46,13 @@ export function assertTypes( } } -export function isPrimitive(value: unknown) { +export function isPrimitive(value: unknown): boolean { return ( value === null || (typeof value !== 'function' && typeof value !== 'object') ) } -export function slash(path: string) { +export function slash(path: string): string { return path.replace(/\\/g, '/') } @@ -93,7 +117,7 @@ function collectOwnProperties( Object.getOwnPropertySymbols(obj).forEach(collect) } -export function getOwnProperties(obj: any) { +export function getOwnProperties(obj: any): (string | symbol)[] { const ownProps = new Set() if (isFinalObj(obj)) { return [] @@ -170,13 +194,13 @@ export function clone( return val } -export function noop() {} +export function noop(): void {} export function objectAttr( source: any, path: string, defaultValue = undefined, -) { +): any { // a[3].b -> a.3.b const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.') let result = source @@ -217,7 +241,7 @@ export function createDefer(): DeferPromise { * toBeAliased('123') * ``` */ -export function getCallLastIndex(code: string) { +export function getCallLastIndex(code: string): number | null { let charIndex = -1 let inString: string | null = null let startedBracers = 0 @@ -255,7 +279,7 @@ export function getCallLastIndex(code: string) { return null } -export function isNegativeNaN(val: number) { +export function isNegativeNaN(val: number): boolean { if (!Number.isNaN(val)) { return false } diff --git a/packages/utils/src/highlight.ts b/packages/utils/src/highlight.ts index 6ab5de815..0f5c86bc8 100644 --- a/packages/utils/src/highlight.ts +++ b/packages/utils/src/highlight.ts @@ -39,7 +39,7 @@ interface HighlightOptions { export function highlight( code: string, options: HighlightOptions = { jsx: false }, -) { +): string { return baseHighlight(code, { jsx: options.jsx, colors: getDefs(options.colors || c), diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 77b3fb300..dce716edf 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,10 +1,50 @@ -export * from './helpers' -export * from './types' -export * from './stringify' -export * from './timers' -export * from './random' -export * from './display' -export * from './constants' -export * from './base' -export * from './offset' -export * from './highlight' +export { + notNullish, + assertTypes, + isPrimitive, + slash, + parseRegexp, + isObject, + getType, + getOwnProperties, + deepClone, + clone, + noop, + objectAttr, + createDefer, + getCallLastIndex, + isNegativeNaN, + createSimpleStackTrace, + toArray, +} from './helpers' +export type { DeferPromise } from './helpers' + +export { getSafeTimers, setSafeTimers } from './timers' +export type { SafeTimers } from './timers' + +export { shuffle } from './random' +export { + stringify, + format, + inspect, + objDisplay, +} from './display' +export { + positionToOffset, + offsetToLineNumber, + lineSplitRE, +} from './offset' +export { highlight } from './highlight' + +export type { + Awaitable, + Nullable, + Arrayable, + ArgumentsType, + MergeInsertions, + DeepMerge, + MutableArray, + Constructable, + ParsedStack, + ErrorWithDiff, +} from './types' diff --git a/packages/utils/src/offset.ts b/packages/utils/src/offset.ts index 8d340281a..f76a007fb 100644 --- a/packages/utils/src/offset.ts +++ b/packages/utils/src/offset.ts @@ -1,4 +1,4 @@ -export const lineSplitRE = /\r?\n/ +export const lineSplitRE: RegExp = /\r?\n/ export function positionToOffset( source: string, diff --git a/packages/utils/src/random.ts b/packages/utils/src/random.ts index 7cd332172..a0ecf2488 100644 --- a/packages/utils/src/random.ts +++ b/packages/utils/src/random.ts @@ -5,7 +5,7 @@ function random(seed: number) { return x - Math.floor(x) } -export function shuffle(array: T[], seed = RealDate.now()): T[] { +export function shuffle(array: T[], seed: number = RealDate.now()): T[] { let length = array.length while (length) { diff --git a/packages/utils/src/source-map.ts b/packages/utils/src/source-map.ts index 99ebab691..c549c2907 100644 --- a/packages/utils/src/source-map.ts +++ b/packages/utils/src/source-map.ts @@ -107,7 +107,7 @@ export function parseSingleFFOrSafariStack(raw: string): ParsedStack | null { } } -export function parseSingleStack(raw: string) { +export function parseSingleStack(raw: string): ParsedStack | null { const line = raw.trim() if (!CHROME_IE_STACK_REGEXP.test(line)) { return parseSingleFFOrSafariStack(line) diff --git a/packages/utils/src/stringify.ts b/packages/utils/src/stringify.ts deleted file mode 100644 index 574d5ef70..000000000 --- a/packages/utils/src/stringify.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { PrettyFormatOptions } from '@vitest/pretty-format' -import { - format as prettyFormat, - plugins as prettyFormatPlugins, -} from '@vitest/pretty-format' - -const { - AsymmetricMatcher, - DOMCollection, - DOMElement, - Immutable, - ReactElement, - ReactTestComponent, -} = prettyFormatPlugins - -const PLUGINS = [ - ReactTestComponent, - ReactElement, - DOMElement, - DOMCollection, - Immutable, - AsymmetricMatcher, -] - -export function stringify( - object: unknown, - maxDepth = 10, - { maxLength, ...options }: PrettyFormatOptions & { maxLength?: number } = {}, -): string { - const MAX_LENGTH = maxLength ?? 10000 - let result - - try { - result = prettyFormat(object, { - maxDepth, - escapeString: false, - // min: true, - plugins: PLUGINS, - ...options, - }) - } - catch { - result = prettyFormat(object, { - callToJSON: false, - maxDepth, - escapeString: false, - // min: true, - plugins: PLUGINS, - ...options, - }) - } - - return result.length >= MAX_LENGTH && maxDepth > 1 - ? stringify(object, Math.floor(maxDepth / 2)) - : result -} diff --git a/packages/utils/src/timers.ts b/packages/utils/src/timers.ts index 1ca76e815..bc95037d6 100644 --- a/packages/utils/src/timers.ts +++ b/packages/utils/src/timers.ts @@ -1,6 +1,16 @@ -import { SAFE_TIMERS_SYMBOL } from './constants' +const SAFE_TIMERS_SYMBOL = Symbol('vitest:SAFE_TIMERS') -export function getSafeTimers() { +export interface SafeTimers { + nextTick: (cb: () => void) => void + setTimeout: typeof setTimeout + setInterval: typeof setInterval + clearInterval: typeof clearInterval + clearTimeout: typeof clearTimeout + setImmediate: typeof setImmediate + clearImmediate: typeof clearImmediate +} + +export function getSafeTimers(): SafeTimers { const { setTimeout: safeSetTimeout, setInterval: safeSetInterval, @@ -24,7 +34,7 @@ export function getSafeTimers() { } } -export function setSafeTimers() { +export function setSafeTimers(): void { const { setTimeout: safeSetTimeout, setInterval: safeSetInterval, diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json index c2166451f..53f9fd0cc 100644 --- a/packages/utils/tsconfig.json +++ b/packages/utils/tsconfig.json @@ -1,5 +1,9 @@ { "extends": "../../tsconfig.base.json", + "compilerOptions": { + "moduleResolution": "Bundler", + "isolatedDeclarations": true + }, "include": ["src/**/*"], "exclude": ["**/dist/**"] } diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts index 5d6677c77..fe8a099cb 100644 --- a/packages/vitest/src/node/reporters/base.ts +++ b/packages/vitest/src/node/reporters/base.ts @@ -12,7 +12,6 @@ import type { } from '../../types' import { getFullName, - getSafeTimers, getSuites, getTestName, getTests, @@ -72,7 +71,7 @@ export abstract class BaseReporter implements Reporter { private _filesInWatchMode = new Map() private _lastRunTimeout = 0 - private _lastRunTimer: NodeJS.Timer | undefined + private _lastRunTimer: NodeJS.Timeout | undefined private _lastRunCount = 0 private _timeStart = new Date() private _offUnhandledRejection?: () => void @@ -216,7 +215,6 @@ export abstract class BaseReporter implements Reporter { ] this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[0]) this._lastRunTimeout = 0 - const { setInterval } = getSafeTimers() this._lastRunTimer = setInterval(() => { this._lastRunTimeout += 1 if (this._lastRunTimeout >= LAST_RUN_TEXTS.length) { @@ -232,7 +230,6 @@ export abstract class BaseReporter implements Reporter { } private resetLastRunLog() { - const { clearInterval } = getSafeTimers() clearInterval(this._lastRunTimer) this._lastRunTimer = undefined this.ctx.logger.logUpdate.clear() -- 2.51.2