fabro/apps/fabro-web/app/lib/test-utils.tsx
Bryan Helmkamp 0a39ba9e06
Shared-checkout parallel execution (recovered from run 01KY7YH7RYCJ1BDVTTP96ZA4HV)
Cumulative implement + simplify_fable diff recovered from the run's meta
branch (fabro/meta/01KY7YH7RYCJ1BDVTTP96ZA4HV, stage 006 diff.patch).
The run validated this tree clean: cargo nextest (7,007 passed), clippy,
fmt, TS client regen + typecheck, web tests (679 passed), docs check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 06:19:11 -04:00

73 lines
2 KiB
TypeScript

import { createElement, type ReactNode } from "react";
import type { EventEnvelope } from "@qltysh/fabro-api-client";
import TestRenderer, { act } from "react-test-renderer";
const IS_REACT_ACT_ENV = "IS_REACT_ACT_ENVIRONMENT" as const;
/**
* Per-test setup for code that uses react-test-renderer:
* - Sets IS_REACT_ACT_ENVIRONMENT (required by act()).
* - Silences react-test-renderer's deprecation warning.
*
* Returns a teardown function; pair with beforeEach/afterEach so the global
* state is scoped to the test rather than leaking process-wide.
*/
export function setupReactTestEnv(): () => void {
type Globals = { [IS_REACT_ACT_ENV]?: boolean };
const globals = globalThis as Globals;
const hadEnv = IS_REACT_ACT_ENV in globals;
const previousEnv = globals[IS_REACT_ACT_ENV];
globals[IS_REACT_ACT_ENV] = true;
const originalConsoleError = console.error;
console.error = ((...args: unknown[]) => {
if (
typeof args[0] === "string" &&
args[0].startsWith("react-test-renderer is deprecated")
) {
return;
}
originalConsoleError(...args);
}) as typeof console.error;
return () => {
console.error = originalConsoleError;
if (hadEnv) {
globals[IS_REACT_ACT_ENV] = previousEnv;
} else {
delete globals[IS_REACT_ACT_ENV];
}
};
}
/** Build an event envelope fixture; override any field via `partial`. */
export function makeEventEnvelope(
seq: number,
partial: Partial<EventEnvelope>,
): EventEnvelope {
return {
seq,
id: `evt-${seq}`,
ts: `2026-04-09T12:00:0${seq}Z`,
run_id: "run-1",
event: "stage.prompt",
...partial,
} as EventEnvelope;
}
export function renderHook<T>(
hook: () => T,
options: { wrapper: React.ComponentType<{ children: ReactNode }> },
): { result: { current: T } } {
const result = { current: undefined as unknown as T };
function HookHost() {
result.current = hook();
return null;
}
act(() => {
TestRenderer.create(
createElement(options.wrapper, null, createElement(HookHost)),
);
});
return { result };
}