k_card/tests/k_phone_portal.spec.js

124 lines
4.9 KiB
JavaScript

/**
* 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");
});
});