Files
ThothII/frontend/e2e/f1.spec.ts
T

69 lines
2.8 KiB
TypeScript

import { expect, test, type Page } from "@playwright/test";
import { createAuthenticationStack } from "./fixtures/auth-stack.mjs";
/**
* E2E F1 loop — hermetic (no VPN, no real Pi, no real Python).
*
* The backend is wired to:
* - fake-tht.mjs : stubs `tht session new/list/show`
* - fake-pi.mjs : delegates to harness/tests/fake_pi/fake_pi_rpc.mjs
* which emits the f1_disambiguation.json scenario (select widget with
* "interpretazione A" / "interpretazione B").
*
* Flow: authenticate locally → focus the new-session composer → fill question → submit (Send)
* → wait for F1 select widget → click an option → assert no error shown.
*/
test.describe.configure({ mode: "serial" });
let stack: Awaited<ReturnType<typeof createAuthenticationStack>>;
test.beforeAll(async () => {
stack = await createAuthenticationStack({ withF1Workspace: true });
await stack.useLocalMode();
});
test.afterAll(async () => {
await stack?.close();
});
async function signInLocally(page: Page): Promise<void> {
const account = stack.localAccount("ordinary");
await page.getByLabel("Username").fill(account.username);
await page.getByLabel("Password", { exact: true }).fill(account.password);
await page.getByRole("button", { name: "Sign in", exact: true }).click();
await expect(page.getByTestId("app-shell")).toBeVisible({ timeout: 30_000 });
}
test("F1 loop: new question → F1 widget → respond", async ({ page }) => {
await page.goto(stack.publicUrl);
await signInLocally(page);
// Focus the composer for a new session.
await page.getByRole("button", { name: "Session", exact: true }).click();
// Fill in the question.
await page.getByLabel("New question").fill("quante cardioversioni nel 2024");
// Submit — the backend creates the session (fake-tht) and spawns Pi (fake-pi).
await page.getByRole("button", { name: "Send", exact: true }).click();
// Wait for the F1 disambiguation select widget to appear.
// The fake-pi emits extension_ui_request → backend bridges to SSE → frontend
// renders SelectWidget with options from f1_disambiguation.json.
await expect(
page.getByRole("button", { name: /interpretazione A/i }),
).toBeVisible({ timeout: 30_000 });
// Both options from the scenario should be present.
await expect(page.getByRole("button", { name: /interpretazione B/i })).toBeVisible();
// Click an option — the frontend POSTs to /sessions/e2e-1/response.
await page.getByRole("button", { name: /interpretazione A/i }).click();
// After responding, the widget should disappear (clearPending) and no error shown.
await expect(
page.getByRole("button", { name: /interpretazione A/i }),
).not.toBeVisible({ timeout: 10_000 });
await expect(page.locator("body")).not.toContainText(/error/i);
});