import { test, expect } from "@playwright/test"; /** * 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: open app → open NewSessionDialog → fill question → submit (Crea) * → wait for F1 select widget → click an option → assert no error shown. */ test("F1 loop: new question → F1 widget → respond", async ({ page }) => { await page.goto("/"); // Open the new-session dialog. await page.getByRole("button", { name: /nuova/i }).click(); // Fill in the question. await page.getByLabel(/domanda/i).fill("quante cardioversioni nel 2024"); // Submit — the backend creates the session (fake-tht) and spawns Pi (fake-pi). await page.getByRole("button", { name: /^crea$/i }).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(/errore/i); });