146 lines
4.2 KiB
TypeScript
146 lines
4.2 KiB
TypeScript
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 (
|
||
<QueryClientProvider client={makeClient()}>{ui}</QueryClientProvider>
|
||
);
|
||
}
|
||
|
||
// (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(<ResultsPanel sessionId="sess-1" />));
|
||
|
||
// 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(<ResultsPanel sessionId="sess-2" />));
|
||
|
||
// 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(<ResultsPanel sessionId="sess-3" />));
|
||
|
||
// 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(<ResultsPanel sessionId="sess-4" />));
|
||
|
||
// 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));
|
||
});
|