import { describe, expect, mock, test } from "bun:test"; import { act, createElement, useState } from "react"; import type { Logger } from "../../../src/web/utils/logger"; import { useLogger } from "../../../src/web/hooks/use-logger"; import { renderWithProviders } from "../test-utils"; function BindingsHookTester({ bindings, onMount, }: { bindings?: Record; onMount: (logger: Logger) => void; }) { const logger = useLogger(bindings); onMount(logger); return null; } function HookTester({ onMount }: { onMount: (logger: Logger) => void }) { const logger = useLogger(); onMount(logger); return null; } describe("useLogger", () => { test("返回 Logger 实例含所有方法", () => { let logger: Logger | undefined; const onMount = (l: Logger) => { logger = l; }; renderWithProviders(createElement(HookTester, { onMount })); expect(logger).toBeDefined(); expect(typeof logger!.debug).toBe("function"); expect(typeof logger!.info).toBe("function"); expect(typeof logger!.warn).toBe("function"); expect(typeof logger!.error).toBe("function"); expect(typeof logger!.child).toBe("function"); expect(typeof logger!.setLevel).toBe("function"); }); test("调用 logger.warn 时静默不抛异常", () => { const warnSpy = mock(() => {}); const origWarn = console.warn; console.warn = warnSpy; let logger: Logger | undefined; renderWithProviders( createElement(HookTester, { onMount: (l: Logger) => { logger = l; }, }), ); expect(() => logger!.warn("测试警告")).not.toThrow(); console.warn = origWarn; expect(warnSpy).toHaveBeenCalled(); }); test("调用 logger.error 时静默不抛异常", () => { const errorSpy = mock(() => {}); const origError = console.error; console.error = errorSpy; let logger: Logger | undefined; renderWithProviders( createElement(HookTester, { onMount: (l: Logger) => { logger = l; }, }), ); expect(() => logger!.error("测试错误")).not.toThrow(); console.error = origError; expect(errorSpy).toHaveBeenCalled(); }); }); describe("useLogger bindings 参数", () => { test("传入 bindings 返回带上下文前缀的 Logger", () => { const spy = mock((..._args: unknown[]) => {}); const origWarn = console.warn; console.warn = spy; let logger: Logger | undefined; renderWithProviders( createElement(BindingsHookTester, { bindings: { component: "TestComp" }, onMount: (l: Logger) => { logger = l; }, }), ); logger!.warn("绑定测试"); console.warn = origWarn; expect(spy).toHaveBeenCalledTimes(1); expect(spy.mock.calls[0]![0]).toMatch(/\[component=TestComp\]/); }); test("无参调用与空 bindings 行为一致", () => { let noBindingsLogger: Logger | undefined; let emptyBindingsLogger: Logger | undefined; const spy = mock((..._args: unknown[]) => {}); const origWarn = console.warn; console.warn = spy; renderWithProviders( createElement(HookTester, { onMount: (l: Logger) => { noBindingsLogger = l; }, }), ); renderWithProviders( createElement(BindingsHookTester, { bindings: {}, onMount: (l: Logger) => { emptyBindingsLogger = l; }, }), ); noBindingsLogger!.warn("a"); emptyBindingsLogger!.warn("b"); console.warn = origWarn; expect(spy).toHaveBeenCalledTimes(2); expect(spy.mock.calls[0]![0]).not.toMatch(/\[component=/); expect(spy.mock.calls[1]![0]).not.toMatch(/\[component=/); }); test("相同 bindings 值多次渲染返回同一 Logger 引用", () => { const refs: Logger[] = []; function StableTester() { const logger = useLogger({ component: "Stable" }); const [, setTick] = useState(0); refs.push(logger); return createElement("button", { onClick: () => setTick((t) => t + 1), }); } const result = renderWithProviders(createElement(StableTester)); const initialCount = refs.length; act(() => { result.getByRole("button").click(); }); act(() => { result.getByRole("button").click(); }); expect(refs.length).toBeGreaterThan(initialCount); for (let i = 1; i < refs.length; i++) { expect(refs[i]).toBe(refs[0]); } }); test("bindings 值变化时返回新 Logger 引用", () => { const refs: Logger[] = []; let currentBindings: Record = { component: "A" }; function DynamicTester() { const logger = useLogger(currentBindings); refs.push(logger); return null; } const { rerender } = renderWithProviders(createElement(DynamicTester)); const countBefore = refs.length; currentBindings = { component: "B" }; rerender(createElement(DynamicTester)); expect(refs.length).toBe(countBefore + 1); expect(refs[refs.length - 1]).not.toBe(refs[0]); }); });