jest-metadata
Version:
🦸♂️ Superhero power for your Jest reporters! 🦸♀️
253 lines (214 loc) • 7.86 kB
text/typescript
import type { Circus } from '@jest/types';
import type { MetadataEventEmitter } from '../metadata';
import { CircusInstanceCache } from './CircusInstanceCache';
export type CircusTestEventHandlerConfig = {
readonly emitter: MetadataEventEmitter;
};
type CircusSyncEventHandler = (event: any, state: Circus.State) => void;
export class EnvironmentEventHandler {
private _instanceCache = new CircusInstanceCache();
private _testFilePath = '';
private readonly _emitter: MetadataEventEmitter;
private readonly _circusEventHandlers: Record<Circus.Event['name'], CircusSyncEventHandler> = {
setup: (_event: Circus.Event & { name: 'setup' }, state: Circus.State) => {
// Auxiliary event for ensuring that the test environment is Jest Circus, not Jest Jasmine.
this._emitter.emit({
type: 'setup',
testFilePath: this._testFilePath,
});
// The root describe block is not emitted by Circus, so we emit it here.
this._emitter.emit({
type: 'start_describe_definition',
testFilePath: this._testFilePath,
describeId: this._instanceCache.getDescribeId(state.rootDescribeBlock),
});
},
include_test_location_in_result: () => {
/* noop */
},
start_describe_definition: (
_event: Circus.Event & { name: 'start_describe_definition' },
state: Circus.State,
) => {
// Safeguard in case Circus ever starts emitting describe blocks.
if (state.currentDescribeBlock === state.rootDescribeBlock) {
return;
}
this._emitter.emit({
type: 'start_describe_definition',
testFilePath: this._testFilePath,
describeId: this._instanceCache.getDescribeId(state.currentDescribeBlock),
});
},
add_hook: (event: Circus.Event & { name: 'add_hook' }) => {
this._emitter.emit({
type: 'add_hook',
testFilePath: this._testFilePath,
hookId: this._instanceCache.getHookId(event.asyncError),
hookType: event.hookType,
});
},
add_test: (event: Circus.Event & { name: 'add_test' }) => {
this._emitter.emit({
type: 'add_test',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.asyncError),
});
},
finish_describe_definition: (
_event: Circus.Event & { name: 'finish_describe_definition' },
state: Circus.State,
) => {
const block = state.currentDescribeBlock;
const lastChild = block.children[block.children.length - 1]!;
this._emitter.emit({
type: 'finish_describe_definition',
testFilePath: this._testFilePath,
describeId: this._instanceCache.getDescribeId(lastChild),
});
},
hook_start: (event: Circus.Event & { name: 'hook_start' }) => {
this._emitter.emit({
type: 'hook_start',
testFilePath: this._testFilePath,
hookId: this._instanceCache.getHookId(event.hook.asyncError),
});
},
hook_success: (event: Circus.Event & { name: 'hook_success' }) => {
this._emitter.emit({
type: 'hook_success',
testFilePath: this._testFilePath,
hookId: this._instanceCache.getHookId(event.hook.asyncError),
});
},
hook_failure: (event: Circus.Event & { name: 'hook_failure' }) => {
this._emitter.emit({
type: 'hook_failure',
testFilePath: this._testFilePath,
hookId: this._instanceCache.getHookId(event.hook.asyncError),
});
},
test_fn_start: (event: Circus.Event & { name: 'test_fn_start' }) => {
this._emitter.emit({
type: 'test_fn_start',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_fn_success: (event: Circus.Event & { name: 'test_fn_success' }) => {
this._emitter.emit({
type: 'test_fn_success',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_fn_failure: (event: Circus.Event & { name: 'test_fn_failure' }) => {
this._emitter.emit({
type: 'test_fn_failure',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_retry: (event: Circus.Event & { name: 'test_retry' }) => {
this._emitter.emit({
type: 'test_retry',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_start: (event: Circus.Event & { name: 'test_start' }) => {
this._emitter.emit({
type: 'test_start',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_started: (event: Circus.Event & { name: 'test_started' }) => {
this._emitter.emit({
type: 'test_started',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_skip: (event: Circus.Event & { name: 'test_skip' }) => {
this._emitter.emit({
type: 'test_skip',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_todo: (event: Circus.Event & { name: 'test_todo' }) => {
this._emitter.emit({
type: 'test_todo',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
test_done: (event: Circus.Event & { name: 'test_done' }) => {
this._emitter.emit({
type: 'test_done',
testFilePath: this._testFilePath,
testId: this._instanceCache.getTestId(event.test.asyncError),
});
},
run_describe_start: (event: Circus.Event & { name: 'run_describe_start' }) => {
this._emitter.emit({
type: 'run_describe_start',
testFilePath: this._testFilePath,
describeId: this._instanceCache.getDescribeId(event.describeBlock),
});
},
run_describe_finish: (event: Circus.Event & { name: 'run_describe_finish' }) => {
this._emitter.emit({
type: 'run_describe_finish',
testFilePath: this._testFilePath,
describeId: this._instanceCache.getDescribeId(event.describeBlock),
});
},
run_start: (_event: Circus.Event & { name: 'run_start' }, state: Circus.State) => {
this._emitter.emit({
type: 'finish_describe_definition',
testFilePath: this._testFilePath,
describeId: this._instanceCache.getDescribeId(state.rootDescribeBlock),
});
this._emitter.emit({
type: 'run_start',
testFilePath: this._testFilePath,
});
},
run_finish: () => {
this._emitter.emit({
type: 'run_finish',
testFilePath: this._testFilePath,
});
},
teardown: () => {
/* no-op */
},
error: () => {
/* no-op */
},
error_handled: () => {
/* no-op - this is a new Jest 30 event, keeping as no-op for now */
},
};
constructor(config: CircusTestEventHandlerConfig) {
this._emitter = config.emitter;
}
get testFilePath(): string {
return this._testFilePath;
}
/**
* Since this class is instantiated only once per the whole test session
* in the global realm (parent or child), we need to reset its instance cache
* and update the test file path when a new test file is started.
* @param testFilePath
*/
handleEnvironmentCreated(testFilePath: string): void {
this._instanceCache = new CircusInstanceCache();
this._testFilePath = testFilePath;
}
handleTestEvent = (event: Circus.Event, state: Circus.State): void => {
return this._circusEventHandlers[event.name]?.(event, state);
};
}