import { CB, BusType, Failure, deferThrow } from 'vest-utils'; import { Bus, RuntimeEvents, TIsolate, VestRuntime } from 'vestjs-runtime'; import { useOmitOptionalFields } from '../../hooks/optional/omitOptionalFields'; import { TIsolateSuite } from '../isolate/IsolateSuite/IsolateSuite'; import { useRunDoneCallbacks, useRunFieldCallbacks, useRunSyncFieldCallbacks, } from '../../suite/runCallbacks'; import { TFieldName } from '../../suiteResult/SuiteResultTypes'; import { useExpireSuiteResultCache, useResetCallbacks, useResetSuite, } from '../Runtime'; import { TestWalker } from '../isolate/IsolateTest/TestWalker'; import { VestTest } from '../isolate/IsolateTest/VestTest'; import { useOnTestStart, useRegisterSubtree } from '../isolate/registerTests'; import { useRegistryBusEvents } from '../test/TestRegistry'; import { Events } from './BusEvents'; // eslint-disable-next-line max-statements, max-lines-per-function export function useInitVestBus() { const VestBus = Bus.useBus(); useRegistryBusEvents(VestBus); // Invalidate suite result cache on test start to prevent stale cache from // affecting test exclusion logic (see https://github.com/ealush/vest/issues/1157). VestBus.on('TEST_RUN_STARTED', () => { useExpireSuiteResultCache(); }); VestBus.on('ISOLATE_ENTER', (isolate: TIsolate) => { if (VestTest.is(isolate)) { useOnTestStart(isolate); } }); VestBus.on('ISOLATE_RECONCILED', (isolate: TIsolate) => { useRegisterSubtree(isolate); }); VestBus.on('ISOLATE_DONE', (isolate: TIsolate) => { if (VestTest.is(isolate)) { VestBus.emit('TEST_COMPLETED', isolate); } }); VestBus.on('TEST_COMPLETED', () => { useExpireSuiteResultCache(); }); VestBus.on('ASYNC_ISOLATE_DONE', (isolate: TIsolate) => { if (VestTest.is(isolate)) { if (!VestTest.isCanceled(isolate).unwrap()) { const { fieldName } = VestTest.getData(isolate); useRunFieldCallbacks(fieldName); useRunDoneCallbacks(); } } if (VestRuntime.useIsStable()) { // When no more async tests are running, emit the done event VestBus.emit('ALL_RUNNING_TESTS_FINISHED'); } }); VestBus.on('DONE_TEST_OMISSION_PASS', () => { /* We NEED to refresh the cache here. Don't ask */ useExpireSuiteResultCache(); }); // Called when all the tests, including async, are done running VestBus.on('ALL_RUNNING_TESTS_FINISHED', () => { // Small optimization. We don't need to run this if there are no async tests // The reason is that we run this function immediately after the suite callback // is run, so if the suite is only comprised of sync tests, we don't need to // run this function twice since we know for a fact the state is up to date if (TestWalker.someTests(VestTest.isAsyncTest)) { useOmitOptionalFields(); } // resolve the suite's promise const root = VestRuntime.useAvailableRoot(); if (root) { root.data.resolver(); } }); VestBus.on('DEFER_THROW', (failure: Failure) => { failure.mapError(deferThrow); }); VestBus.on('RESET_FIELD', (fieldName: TFieldName) => { useExpireSuiteResultCache(); TestWalker.resetField(fieldName); }); VestBus.on('SUITE_RUN_STARTED', () => {}); VestBus.on('INITIALIZING_CALLBACKS', () => { useExpireSuiteResultCache(); useResetCallbacks(); }); VestBus.on('SUITE_CALLBACK_RUN_FINISHED', () => { useExpireSuiteResultCache(); if (VestRuntime.useIsStable()) { // When no more async tests are running, emit the done event VestBus.emit('ALL_RUNNING_TESTS_FINISHED'); } useOmitOptionalFields(); useRunSyncFieldCallbacks(); useRunDoneCallbacks(); }); VestBus.on('REMOVE_FIELD', (fieldName: TFieldName) => { useExpireSuiteResultCache(); TestWalker.removeTestByFieldName(fieldName); }); VestBus.on('RESET_SUITE', () => { useResetSuite(); }); return { subscribe, }; function subscribe( event: T, cb: (payload: VestEvents[T]) => void, ): CB; function subscribe(cb: CB): CB; function subscribe( ...args: [event: keyof VestEvents, cb: CB] | [cb: CB] ): CB { const [cb, event] = args.reverse() as [CB, keyof VestEvents]; // @ts-ignore - Argument type mismatch due to wildcard handling complexity return VestBus.on(event ?? '*', () => { cb(); }).off; } } export function useEmit(): BusType['emit']; export function useEmit( event: T, ...args: VestEvents[T] extends void ? [payload?: VestEvents[T]] : [payload: VestEvents[T]] ): void; export function useEmit(event?: keyof VestEvents, data?: any): any { return Bus.useEmit(event!, data); } export function usePrepareEmitter( event: T, ): (arg: VestEvents[T]) => void { return Bus.usePrepareEmitter(event); } export type VestEvents = Events & RuntimeEvents; export type Subscribe = { ( event: T, cb: (payload: VestEvents[T]) => void, ): CB; (cb: CB): CB; };