Skip to content

Commit

Permalink
refactor(runner): add docs to @vitest/runner, remove barrel files (#6126
Browse files Browse the repository at this point in the history
)
  • Loading branch information
sheremet-va authored Jul 15, 2024
1 parent f55dc00 commit f1ef2f6
Show file tree
Hide file tree
Showing 41 changed files with 593 additions and 216 deletions.
11 changes: 9 additions & 2 deletions packages/pretty-format/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion packages/pretty-format/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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/**"]
Expand Down
2 changes: 1 addition & 1 deletion packages/runner/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
2 changes: 1 addition & 1 deletion packages/runner/src/collect.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
6 changes: 3 additions & 3 deletions packages/runner/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
SuiteCollector,
TaskContext,
Test,
} from './types'
} from './types/tasks'
import type { VitestRunner } from './types/runner'
import { PendingError } from './errors'

Expand All @@ -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<void>,
) {
): Promise<void> {
const prev = collectorContext.currentSuite
collectorContext.currentSuite = suite
await fn()
Expand Down
2 changes: 1 addition & 1 deletion packages/runner/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TaskBase } from './types'
import type { TaskBase } from './types/tasks'

export class PendingError extends Error {
public code = 'VITEST_PENDING'
Expand Down
10 changes: 6 additions & 4 deletions packages/runner/src/fixture.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,7 +18,9 @@ export interface FixtureItem extends FixtureOptions {
export function mergeContextFixtures(
fixtures: Record<string, any>,
context: { fixtures?: FixtureItem[] } = {},
) {
): {
fixtures?: FixtureItem[]
} {
const fixtureOptionKeys = ['auto']
const fixtureArray: FixtureItem[] = Object.entries(fixtures).map(
([prop, value]) => {
Expand Down Expand Up @@ -69,7 +71,7 @@ const cleanupFnArrayMap = new Map<
Array<() => void | Promise<void>>
>()

export async function callFixtureCleanup(context: TestContext) {
export async function callFixtureCleanup(context: TestContext): Promise<void> {
const cleanupFnArray = cleanupFnArrayMap.get(context) ?? []
for (const cleanup of cleanupFnArray.reverse()) {
await cleanup()
Expand All @@ -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

Expand Down
149 changes: 130 additions & 19 deletions packages/runner/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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<ExtraContext = object>(
fn: SuiteHooks<ExtraContext>['beforeEach'][0],
fn: BeforeEachListener<ExtraContext>,
timeout?: number,
) {
): void {
return getCurrentSuite<ExtraContext>().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<ExtraContext = object>(
fn: SuiteHooks<ExtraContext>['afterEach'][0],
fn: AfterEachListener<ExtraContext>,
timeout?: number,
) {
): void {
return getCurrentSuite<ExtraContext>().on(
'afterEach',
withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true),
)
}

export const onTestFailed = createTestHook<OnTestFailedHandler>(
/**
* 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<OnTestFailedHandler> = 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<OnTestFinishedHandler>(
/**
* 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<OnTestFinishedHandler> = createTestHook(
'onTestFinished',
(test, handler) => {
(test, handler, timeout) => {
test.onFinished ||= []
test.onFinished.push(handler)
test.onFinished.push(
withTimeout(handler, timeout ?? getDefaultHookTimeout(), true),
)
},
)

function createTestHook<T>(
name: string,
handler: (test: TaskPopulated, handler: T) => void,
) {
return (fn: T) => {
handler: (test: TaskPopulated, handler: T, timeout?: number) => void,
): TaskHook<T> {
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)
}
}
3 changes: 2 additions & 1 deletion packages/runner/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
8 changes: 4 additions & 4 deletions packages/runner/src/map.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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
const fnMap = new WeakMap()
const fixtureMap = new WeakMap()
const hooksMap = new WeakMap()

export function setFn(key: Test | Custom, fn: () => Awaitable<void>) {
export function setFn(key: Test | Custom, fn: () => Awaitable<void>): void {
fnMap.set(key, fn)
}

Expand All @@ -18,15 +18,15 @@ export function getFn<Task = Test | Custom>(key: Task): () => Awaitable<void> {
export function setFixture(
key: TestContext,
fixture: FixtureItem[] | undefined,
) {
): void {
fixtureMap.set(key, fixture)
}

export function getFixture<Context = TestContext>(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)
}

Expand Down
Loading

0 comments on commit f1ef2f6

Please sign in to comment.