import { vi } from "vitest"; import type { OutputRuntimeEnv } from "../runtime.js"; import type { MockFn } from "../test-utils/vitest-mock-fn.js"; export type CliMockOutputRuntime = OutputRuntimeEnv & { log: MockFn; error: MockFn; exit: MockFn; writeJson: MockFn; writeStdout: MockFn; }; export type CliRuntimeCapture = { runtimeLogs: string[]; runtimeErrors: string[]; defaultRuntime: CliMockOutputRuntime; resetRuntimeCapture: () => void; }; type MockCallsWithFirstArg = { mock: { calls: Array<[unknown, ...unknown[]]>; }; }; export function normalizeRuntimeStdout(value: string): string { return value.endsWith("\n") ? value.slice(0, -1) : value; } export function stringifyRuntimeJson(value: unknown, space = 2): string { return JSON.stringify(value, null, space > 0 ? space : undefined); } export function createCliRuntimeCapture(): CliRuntimeCapture { const runtimeLogs: string[] = []; const runtimeErrors: string[] = []; const stringifyArgs = (args: unknown[]) => args.map((value) => String(value)).join(" "); const defaultRuntime: CliMockOutputRuntime = { log: vi.fn((...args: unknown[]) => { runtimeLogs.push(stringifyArgs(args)); }), error: vi.fn((...args: unknown[]) => { runtimeErrors.push(stringifyArgs(args)); }), writeStdout: vi.fn((value: string) => { defaultRuntime.log(normalizeRuntimeStdout(value)); }), writeJson: vi.fn((value: unknown, space = 2) => { defaultRuntime.log(stringifyRuntimeJson(value, space)); }), exit: vi.fn((code: number) => { throw new Error(`__exit__:${code}`); }), }; return { runtimeLogs, runtimeErrors, defaultRuntime, resetRuntimeCapture: () => { runtimeLogs.length = 0; runtimeErrors.length = 0; }, }; } export async function mockRuntimeModule( importOriginal: () => Promise, defaultRuntime: TModule["defaultRuntime"], ): Promise { const actual = await importOriginal(); return { ...actual, defaultRuntime: { ...actual.defaultRuntime, ...defaultRuntime, }, }; } export function spyRuntimeLogs(runtime: Pick) { return vi.spyOn(runtime, "log").mockImplementation(() => {}); } export function spyRuntimeErrors(runtime: Pick) { return vi.spyOn(runtime, "error").mockImplementation(() => {}); } export function spyRuntimeJson(runtime: Pick) { return vi.spyOn(runtime, "writeJson").mockImplementation(() => {}); } export function firstWrittenJsonArg(writeJson: MockCallsWithFirstArg): T | null { return (writeJson.mock.calls[0]?.[0] ?? null) as T | null; }