Files
ThothII/frontend/src/viewers/SqlViewer.test.tsx
T

130 lines
4.4 KiB
TypeScript

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('<pre data-testid="hl">SELECT 1</pre>'),
}));
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<typeof vi.fn>;
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(<SqlViewer blocks={blocks} />);
// 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(<SqlViewer blocks={blocks} />);
// 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(<SqlViewer blocks={blocks} />);
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(<SqlViewer blocks={[blocks[0]]} />);
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(<SqlViewer blocks={[blocks[0]]} />);
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(<SqlViewer blocks={[blocks[0]]} />);
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();
});