import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { http, HttpResponse } from "msw"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { server } from "../test/msw"; import { ResultsPanel } from "./ResultsPanel"; const BASE = "/api"; function makeClient() { return new QueryClient({ defaultOptions: { queries: { retry: false } }, }); } function wrap(ui: React.ReactElement) { return ( {ui} ); } // (a) 2-col × 2-row preview → AGGrid renders cell values test("(a) renders grid cells for a 2×2 preview result", async () => { server.use( http.post(`${BASE}/sessions/sess-1/sql/preview`, () => HttpResponse.json({ columns: ["name", "age"], rows: [ ["Alice", 30], ["Bob", 25], ], execution_ms: 5, truncated: true, limit: 10, offset: 0, }) ) ); render(wrap()); // Wait for data to load; check known cell values await screen.findByText("Alice"); expect(screen.getByText("Bob")).toBeInTheDocument(); expect(screen.getByText("30")).toBeInTheDocument(); expect(screen.getByText("25")).toBeInTheDocument(); // Grid must be present, not bold scalar expect(screen.queryByTestId("scalar-value")).not.toBeInTheDocument(); // truncated:true → show the "results truncated" indicator expect(screen.getByText(/results truncated/i)).toBeInTheDocument(); }); // (b) 1×1 preview → bold scalar shown, no grid test("(b) renders bold scalar for a 1×1 result", async () => { server.use( http.post(`${BASE}/sessions/sess-2/sql/preview`, () => HttpResponse.json({ columns: ["count"], rows: [[42]], execution_ms: 2, truncated: false, limit: 10, offset: 0, }) ) ); render(wrap()); // Should show the scalar value in bold const bold = await screen.findByTestId("scalar-value"); expect(bold).toHaveTextContent("42"); // No grid expect(screen.queryByRole("treegrid")).not.toBeInTheDocument(); expect(screen.queryByRole("grid")).not.toBeInTheDocument(); }); // (c) clicking "Esporta CSV" calls the export endpoint test("(c) Esporta CSV button calls the export endpoint", async () => { let exportCalled = false; server.use( http.post(`${BASE}/sessions/sess-3/sql/preview`, () => HttpResponse.json({ columns: ["id"], rows: [[1], [2]], execution_ms: 3, truncated: false, limit: 10, offset: 0, }) ), http.post(`${BASE}/sessions/sess-3/sql/export`, () => { exportCalled = true; return HttpResponse.json({ path: "/tmp/export.csv" }); }) ); const user = userEvent.setup(); render(wrap()); // Wait for preview to load (use role to avoid ambiguity with paging panel) await waitFor(() => expect(screen.getAllByRole("gridcell")).not.toHaveLength(0)); const btn = screen.getByRole("button", { name: /export csv/i }); await user.click(btn); await waitFor(() => expect(exportCalled).toBe(true)); }); // (d) selecting "tutti" drives sqlPreview with the large/unlimited limit test("(d) selecting tutti re-fetches preview with the large limit", async () => { const limits: unknown[] = []; server.use( http.post(`${BASE}/sessions/sess-4/sql/preview`, async ({ request }) => { const body = (await request.json()) as { limit?: number }; limits.push(body.limit); return HttpResponse.json({ columns: ["id"], rows: [[1], [2]], execution_ms: 3, truncated: false, limit: body.limit ?? 10, offset: 0, }); }) ); const user = userEvent.setup(); render(wrap()); // Initial fetch uses limit 10 await waitFor(() => expect(limits).toContain(10)); // Select "all" const select = screen.getByLabelText(/rows to display/i); await user.selectOptions(select, "all"); // A re-fetch with the large/unlimited limit must occur await waitFor(() => expect(limits.some((l) => (l as number) > 10)).toBe(true)); });