/** * Playwright acceptance test for the k_phone portal (Component 2, port 8771). * * Run: * K_PHONE_BASE_URL=http://192.168.x.x:8771 npx playwright test tests/k_phone_portal.spec.js * * Env vars: * K_PHONE_BASE_URL Base URL of the k_phone proxy service (default: http://127.0.0.1:8771) * CARD_REGISTRATION_TIMEOUT_MS Timeout for makeCredential card step (default: 90000) * CARD_LOGIN_TIMEOUT_MS Timeout for getAssertion card step (default: 90000) * PW_HEADLESS Set to "1" for headless mode * * Constraint: the test does not read the Android log — all assertions are * made against visible DOM state and the #log pre element. */ const { test, expect } = require("@playwright/test"); const BASE_URL = process.env.K_PHONE_BASE_URL || "http://127.0.0.1:8771"; const registrationTimeoutMs = Number(process.env.CARD_REGISTRATION_TIMEOUT_MS || "90000"); const loginTimeoutMs = Number(process.env.CARD_LOGIN_TIMEOUT_MS || "90000"); function uniqueUsername() { return `pw_${Date.now().toString(36)}`; } async function waitForLog(page, expectedText, timeoutMs = 10_000) { await expect(page.locator("#log")).toContainText(expectedText, { timeout: timeoutMs }); } test.describe("k_phone portal regression", () => { test( "enrolls, logs in, checks session status, logs out, and deletes user", async ({ page }) => { const username = uniqueUsername(); test.setTimeout(registrationTimeoutMs + loginTimeoutMs + 60_000); await page.goto(BASE_URL + "/"); await expect( page.getByRole("heading", { name: "ChromeCard k_phone Portal" }) ).toBeVisible(); // Clear any leftover localStorage from a previous session so the test // starts from a clean slate regardless of browser profile state. await page.evaluate(() => localStorage.clear()); await page.reload(); await test.step("Initial state is unauthenticated", async () => { await expect(page.locator("#storedUser")).toHaveText("none"); await expect(page.locator("#sessionActive")).toHaveText("no"); }); await test.step("Enroll user", async () => { await page.locator("#username").fill(username); await page.locator("#displayName").fill("Playwright Test"); // Card step: makeCredential — touch user fingerprint on ChromeCard. await page.locator("#enrollBtn").click(); await waitForLog(page, "Enrolled", registrationTimeoutMs); await expect(page.locator("#storedUser")).toHaveText(username); }); await test.step("Login", async () => { // Card step: getAssertion — touch user fingerprint on ChromeCard. await page.locator("#loginBtn").click(); await waitForLog(page, "Login ok", loginTimeoutMs); await expect(page.locator("#sessionActive")).toHaveText("yes"); }); await test.step("Session status reflects active session", async () => { await page.locator("#statusBtn").click(); await waitForLog(page, "Session status"); }); await test.step("List users includes enrolled user", async () => { await page.locator("#listBtn").click(); await waitForLog(page, username); }); await test.step("Logout clears session", async () => { await page.locator("#logoutBtn").click(); // "Logout" is a substring of "Logout failed", so assert the semantic // outcome (sessionActive → no) rather than the log message text. await expect(page.locator("#sessionActive")).toHaveText("no", { timeout: 10_000, }); }); await test.step("Delete user clears stored identity", async () => { await page.locator("#deleteBtn").click(); // "Deleted" is not a substring of "Delete failed" — safe to match. await waitForLog(page, "Deleted"); await expect(page.locator("#storedUser")).toHaveText("none"); await expect(page.locator("#sessionActive")).toHaveText("no"); }); } ); test("enrollment failure is surfaced in log", async ({ page }) => { await page.goto(BASE_URL + "/"); await page.evaluate(() => localStorage.clear()); await page.reload(); // Submit enroll with an empty username — server must reject it. await page.locator("#username").fill(""); await page.locator("#enrollBtn").click(); await waitForLog(page, "Enroll failed"); // No username must have been stored on failure. await expect(page.locator("#storedUser")).toHaveText("none"); }); test("login without enrollment fails gracefully", async ({ page }) => { await page.goto(BASE_URL + "/"); await page.evaluate(() => localStorage.clear()); await page.reload(); // Attempt login with a username that is not enrolled. await page.locator("#username").fill("no_such_user_pw"); await page.locator("#loginBtn").click(); await waitForLog(page, "Login failed"); await expect(page.locator("#sessionActive")).toHaveText("no"); }); });