130 lines
4.4 KiB
TypeScript
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();
|
|
});
|