import { afterEach, expect, test } from "bun:test"; import { act, useState } from "react"; import { Box } from "../ui"; import { createTestControls, emitKeypress, testRender } from "../renderers/opentui/test-utils"; import { PaneFooterBar, PaneFooterProvider, PaneFooterScope, usePaneFooter, type CombinedPaneFooter } from "./layout/pane/footer"; import { usePaneNoticeFooter, type UsePaneNoticeFooterOptions } from "./use-pane-notice-footer"; let setup: Awaited> | undefined; let update!: (next: Partial) => void; let footer: CombinedPaneFooter; const controls = createTestControls(() => setup!); type Options = UsePaneNoticeFooterOptions & { active: boolean }; function Registration({ options }: { options: Options }) { usePaneNoticeFooter(options); usePaneFooter("existing", () => ({ hints: [{ id: "series", key: "s", label: "eries" }] }), []); return null; } function Harness({ notices = [" AMD: publication dates unavailable. ", "AMD: publication dates unavailable.", " "] }: { notices?: string[] }) { const [options, setOptions] = useState({ registrationId: "notice-test", notices, focused: true, active: true }); update = (next) => setOptions((current) => ({ ...current, ...next })); return {(current) => { footer = current; return ; }}; } async function change(next: Partial) { await act(async () => update(next)); await controls.renderFrames(3); } async function key(name: string, modifiers = {}) { await emitKeypress(setup!, { name, ...modifiers }, { trackPropagation: true }); await controls.renderFrames(3); } afterEach(async () => { if (setup) await act(async () => setup!.renderer.destroy()); setup = undefined; }); test("compact warnings disclose by mouse, follow the current data and vanish without disabling existing actions", async () => { await act(async () => { setup = await testRender(, { width: 80, height: 28 }); }); await controls.renderFrames(3); expect(setup!.captureCharFrame()).toContain("⚠"); expect(setup!.captureCharFrame()).toContain("[s]eries"); expect(setup!.captureCharFrame()).not.toContain("publication"); const retained = footer.info[0]!.onPress!; await act(async () => { await setup!.mockMouse.click(setup!.captureCharFrame().split("\n")[0]!.indexOf("⚠"), 0); }); await controls.waitForFrameToContain("publication dates unavailable"); expect(setup!.captureCharFrame().match(/publication dates unavailable/g)?.length).toBe(1); const dialogLines = setup!.captureCharFrame().split("\n"); expect(dialogLines.findIndex((line) => line.includes("Close")) - dialogLines.findIndex((line) => line.includes("Data warnings"))).toBeLessThanOrEqual(5); await change({ notices: ["MSFT: stale provider observation."] }); expect(setup!.captureCharFrame()).not.toContain("publication"); await act(async () => retained()); await controls.waitForFrameToContain("MSFT: stale provider observation."); await controls.clickFrameText("Close"); await controls.renderFrames(3); expect(setup!.captureCharFrame()).not.toContain("stale provider"); await change({ notices: [" "] }); await act(async () => retained()); await key("!"); expect(setup!.captureCharFrame()).not.toContain("⚠"); expect(setup!.captureCharFrame()).toContain("[s]eries"); expect(footer.info).toEqual([]); }); test("notice shortcut respects focus, inactive scopes, enabled state and an already open dialog", async () => { await act(async () => { setup = await testRender(, { width: 80, height: 28 }); }); await controls.renderFrames(3); for (const disabled of [{ focused: false }, { focused: true, active: false }, { active: true, enabled: false }]) { await change(disabled); await key("!"); expect(setup!.captureCharFrame()).not.toContain("publication"); } await change({ enabled: true }); await key("!", { ctrl: true }); expect(setup!.captureCharFrame()).not.toContain("publication"); await key("!"); await controls.waitForFrameToContain("publication dates unavailable"); await key("!"); await key("escape"); expect(setup!.captureCharFrame()).not.toContain("publication"); await key("!"); await controls.waitForFrameToContain("publication dates unavailable"); await change({ active: false }); expect(setup!.captureCharFrame()).not.toContain("publication"); }); test("long warning details remain scrollable to their final observation", async () => { const notices = Array.from({ length: 40 }, (_, index) => `Observation ${index + 1}: provider timestamp unavailable.`); await act(async () => { setup = await testRender(, { width: 48, height: 20 }); }); await controls.renderFrames(3); await key("!"); await controls.waitForFrameToContain("Observation 1:"); expect(setup!.captureCharFrame()).not.toContain("Observation 40:"); await key("end"); expect(setup!.captureCharFrame()).toContain("Observation 40:"); await key("home"); expect(setup!.captureCharFrame()).toContain("Observation 1:"); await key("enter"); expect(setup!.captureCharFrame()).not.toContain("Observation 1:"); }); test("narrow panes keep warning disclosure visible beside overflowing action hints", async () => { await act(async () => { setup = await testRender( {} }], hints: [{ id: "series", key: "s", label: "eries" }, { id: "indicators", key: "i", label: "ndicators" }, { id: "share", key: "x", label: " share" }], }} />, { width: 18, height: 1 }); }); await controls.renderFrames(3); expect(setup!.captureCharFrame()).toContain("⚠"); });