// @vitest-environment jsdom import React, { act } from "react"; import { createRoot } from "react-dom/client"; import { describe, it, expect, beforeAll, beforeEach, afterEach, vi } from "vitest"; import type { CompressionRunModel } from "@/app/(dashboard)/dashboard/compression/studio/compressionFlowModel"; // ── Polyfill ResizeObserver (required by ReactFlow) ─────────────────────── beforeAll(() => { globalThis.ResizeObserver = class { observe() {} unobserve() {} disconnect() {} } as unknown as typeof ResizeObserver; }); // ── Mocks ───────────────────────────────────────────────────────────────── // Stub @xyflow/react so ReactFlow renders without canvas/DOM measurement APIs vi.mock("@xyflow/react", async () => { const actual = (await vi.importActual("@xyflow/react")) as Record; return { ...actual, Handle: (_props: Record) => null, Position: { Left: "left", Right: "right", Top: "top", Bottom: "bottom" }, }; }); // ── Import after mocks ───────────────────────────────────────────────────── const { CompressionCockpit } = await import("@/app/(dashboard)/dashboard/compression/studio/CompressionCockpit"); // ── Helpers ─────────────────────────────────────────────────────────────── const containers: HTMLElement[] = []; function mount(ui: React.ReactElement): HTMLElement { const container = document.createElement("div"); document.body.appendChild(container); containers.push(container); const root = createRoot(container); act(() => { root.render(ui); }); return container; } function click(el: Element | null): void { act(() => { el?.dispatchEvent(new MouseEvent("click", { bubbles: true })); }); } beforeEach(() => { ( globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean } ).IS_REACT_ACT_ENVIRONMENT = true; }); afterEach(() => { while (containers.length > 0) { containers.pop()?.remove(); } document.body.innerHTML = ""; }); // ── Sample run ──────────────────────────────────────────────────────────── const SAMPLE_RUN: CompressionRunModel = { requestId: "test-req-001", comboId: "daily-cascade", mode: "stacked", originalTokens: 12480, compressedTokens: 6090, savingsPercent: 51.2, timestamp: 1718000000000, steps: [ { engine: "rtk", originalTokens: 12480, compressedTokens: 9734, savingsPercent: 22.0, techniquesUsed: ["strip-comments", "shell-filter"], durationMs: 1.8, }, { engine: "headroom", originalTokens: 9734, compressedTokens: 8524, savingsPercent: 12.4, techniquesUsed: ["smartcrusher"], durationMs: 0.9, }, { engine: "caveman", originalTokens: 8524, compressedTokens: 6896, savingsPercent: 19.1, techniquesUsed: ["pt-BR", "filler-drop"], durationMs: 0.6, }, ], }; // ── Tests ───────────────────────────────────────────────────────────────── describe("CompressionCockpit", () => { it("renders the cockpit wrapper when a run is provided", () => { const container = mount(); expect(container.querySelector("[data-testid='compression-cockpit']")).toBeTruthy(); }); it("renders the ReactFlow canvas", () => { const container = mount(); expect(container.querySelector(".react-flow")).toBeTruthy(); }); it("shows the mode in the header", () => { const container = mount(); expect(container.textContent).toContain("stacked"); }); it("shows the comboId in the header", () => { const container = mount(); expect(container.textContent).toContain("daily-cascade"); }); it("shows the total savings percentage in the header", () => { const container = mount(); expect(container.textContent).toContain("51.2"); }); it("shows engine names (from node labels via ReactFlow)", () => { const container = mount(); // The engine names appear in the header text even if ReactFlow node internals // don't render in jsdom — we also check the header region. const text = container.textContent ?? ""; // At minimum the run metadata renders correctly expect(text).toContain("test-req-001"); }); it("renders the empty state when no run is given", () => { const container = mount(); expect(container.querySelector("[data-testid='compression-cockpit-empty']")).toBeTruthy(); expect(container.textContent).toContain("No compression run available"); }); it("renders replay controls when a run is provided", () => { const container = mount(); // Replay button present const text = container.textContent ?? ""; expect(text).toContain("Replay"); }); it("offers a Canvas/Waterfall view toggle", () => { const container = mount(); expect(container.querySelector("[data-testid='cockpit-view-canvas']")).toBeTruthy(); expect(container.querySelector("[data-testid='cockpit-view-waterfall']")).toBeTruthy(); }); it("switches to the waterfall inspector and back to the canvas", () => { const container = mount(); // Default view is the ReactFlow canvas, waterfall hidden. expect(container.querySelector(".react-flow")).toBeTruthy(); expect(container.querySelector("[data-testid='waterfall-inspector']")).toBeFalsy(); // Switch to the waterfall (A1) view — the previously-orphan component is now reachable. click(container.querySelector("[data-testid='cockpit-view-waterfall']")); expect(container.querySelector("[data-testid='waterfall-inspector']")).toBeTruthy(); expect(container.querySelector(".react-flow")).toBeFalsy(); expect( container.querySelector("[data-testid='waterfall-total-savings']")?.textContent ).toContain("51.2"); // Switch back to the canvas. click(container.querySelector("[data-testid='cockpit-view-canvas']")); expect(container.querySelector(".react-flow")).toBeTruthy(); expect(container.querySelector("[data-testid='waterfall-inspector']")).toBeFalsy(); }); });