Files
ThothII/frontend/src/viewers/ResultsPanel.test.tsx
T
marcopanandClaude Opus 4.8 bcaf6708de feat(frontend): English UI + 'Datamart Builder with Human In The Loop' tagline
The interface is now fully English regardless of the chat language (session
titles stay in whatever language the user typed). Changed the right-rail
subtitle to 'Datamart Builder with Human In The Loop'. Translated all chrome:
new-question/settings dialogs, workflow phase strip (F1-F8), session rail,
steer input, widgets (select/multiselect/freetext/fallback/reserved controls)
and viewers (SQL, results, schema linking). Test matchers updated in lockstep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:27:11 +02:00

146 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = "http://localhost:8787";
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));
});