import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { SqlViewer } from "./SqlViewer"; vi.mock("./highlight", () => ({ highlightSql: vi.fn().mockResolvedValue('
SELECT 1
'), })); const blocks = [ { name: "base_orders", fields: 5, testStatus: "passed" as const, sql: "SELECT id, amount FROM orders", comments: { amount: "importo lordo" }, }, { name: "final_query", fields: 3, testStatus: "failed" as const, sql: "SELECT * FROM base_orders", }, ]; let writeText: ReturnType; beforeEach(() => { writeText = vi.fn().mockResolvedValue(undefined); Object.defineProperty(navigator, "clipboard", { configurable: true, value: { writeText }, }); }); test("(a) renders headers with name, field count, and test-status badge", async () => { render(); // Drain the pending highlightSql microtasks inside act() before asserting, // so no React act() warning is emitted (pristine output). await waitFor(() => expect(screen.getAllByTestId("hl")).toHaveLength(2)); // Headers should be visible expect(screen.getByText("base_orders")).toBeInTheDocument(); expect(screen.getByText("final_query")).toBeInTheDocument(); // Field counts expect(screen.getByText(/5 campi/i)).toBeInTheDocument(); expect(screen.getByText(/3 campi/i)).toBeInTheDocument(); // Test-status badges expect(screen.getByText(/passed/i)).toBeInTheDocument(); expect(screen.getByText(/failed/i)).toBeInTheDocument(); }); test("(b) bodies start expanded and clicking header collapses then re-expands", async () => { const user = userEvent.setup(); render(); // Both highlighted bodies should appear after async highlight resolves await waitFor(() => { expect(screen.getAllByTestId("hl")).toHaveLength(2); }); // Collapse the first block by clicking its header const firstHeader = screen .getAllByRole("button", { name: /base_orders/i }) .find((button) => button.hasAttribute("aria-expanded")); expect(firstHeader).toBeDefined(); if (!firstHeader) throw new Error("Missing SQL collapse control"); await user.click(firstHeader); // Now only one highlighted block visible await waitFor(() => { expect(screen.getAllByTestId("hl")).toHaveLength(1); }); // Re-expand await user.click(firstHeader); await waitFor(() => { expect(screen.getAllByTestId("hl")).toHaveLength(2); }); }); test("(c) layout toggle flips its label between Orizzontale and Verticale", async () => { render(); await waitFor(() => expect(screen.getAllByTestId("hl")).toHaveLength(2)); // Default layout is vertical, so the toggle offers "Orizzontale". const toggle = screen.getByRole("button", { name: /horizontal/i }); expect(toggle).toBeInTheDocument(); await userEvent.click(toggle); // After switching to horizontal, the toggle now offers "Verticale". expect( screen.getByRole("button", { name: /vertical/i }) ).toBeInTheDocument(); expect( screen.queryByRole("button", { name: /horizontal/i }) ).not.toBeInTheDocument(); }); test("a single SQL block hides the multi-block layout toggle", async () => { render(); await waitFor(() => expect(screen.getByTestId("hl")).toBeInTheDocument()); expect(screen.queryByRole("button", { name: /horizontal/i })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /vertical/i })).not.toBeInTheDocument(); }); test("copies the exact SQL from an always-available accessible control", async () => { render(); await waitFor(() => expect(screen.getByTestId("hl")).toBeInTheDocument()); await userEvent.click(screen.getByRole("button", { name: /copy sql for base_orders/i })); expect(writeText).toHaveBeenCalledWith("SELECT id, amount FROM orders"); expect(await screen.findByText("Copied")).toBeInTheDocument(); }); test("reports clipboard rejection and keeps the SQL visible", async () => { writeText.mockRejectedValueOnce(new Error("clipboard unavailable")); render(); await waitFor(() => expect(screen.getByTestId("hl")).toBeInTheDocument()); await userEvent.click(screen.getByRole("button", { name: /copy sql for base_orders/i })); expect(await screen.findByText("Copy failed")).toBeInTheDocument(); expect(screen.getByTestId("hl")).toBeInTheDocument(); });