import { expect, test } from "@playwright/test"; import { mockApi } from "./support/network.js"; const USER_PASSKEY_BUTTON = "#passkey-login-button"; const SUBUSER_PASSKEY_BUTTON = "#subuser-passkey-login-button"; async function prepareUserPasskeyPage(page) { await mockApi(page, { authenticated: false }); await page.goto("/login"); } async function prepareSubuserPasskeyPage(page) { await mockApi(page, { authenticated: false }); await page.goto("/login/driver"); } async function stubPendingPasskeyFlow(page) { await page.route(/\/auth\/passkey\/challenge$/, async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ data: { challenge_token: "passkey-challenge-token", publicKey: { challenge: "AA", timeout: 60000, userVerification: "preferred", allowCredentials: [], }, }, }), }); }); await page.evaluate(() => { const existingCredentials = navigator.credentials || {}; Object.defineProperty(navigator, "credentials", { configurable: true, value: { ...existingCredentials, get: () => new Promise(() => {}), }, }); }); } // User Passkey Tests test.describe("[AUTH][Passkey][User]", () => { test("should display passkey login button on user login page when supported", async ({ page }) => { await prepareUserPasskeyPage(page); const passkeyButton = page.locator(USER_PASSKEY_BUTTON); const isVisible = await passkeyButton.isVisible(); if (isVisible) { await expect(passkeyButton).toBeVisible(); await expect(passkeyButton.locator(".fa-fingerprint")).toBeVisible(); } }); test("should have passkey button with fingerprint icon", async ({ page }) => { await prepareUserPasskeyPage(page); const passkeyButton = page.locator(USER_PASSKEY_BUTTON); if (!(await passkeyButton.isVisible())) { test.skip(); return; } await expect(passkeyButton.locator(".fa-fingerprint")).toBeVisible(); }); test("should display loading spinner when passkey login is clicked", async ({ page }) => { await prepareUserPasskeyPage(page); await stubPendingPasskeyFlow(page); const passkeyButton = page.locator(USER_PASSKEY_BUTTON); if (!(await passkeyButton.isVisible())) { test.skip(); return; } await passkeyButton.click(); await expect(passkeyButton.locator(".fa-spinner")).toBeVisible(); }); test("should not disable other login options when passkey is available", async ({ page }) => { await prepareUserPasskeyPage(page); await expect(page.getByTestId("login-customer-number")).toBeVisible(); await expect(page.getByTestId("login-password")).toBeVisible(); await expect(page.getByTestId("login-submit")).toBeVisible(); await expect(page.getByTestId("login-submit")).not.toBeDisabled(); }); test("should show QR code login option alongside passkey", async ({ page }) => { await prepareUserPasskeyPage(page); await expect(page.locator('a[href="/login/qr"]')).toBeVisible(); }); }); // Subuser Passkey Tests test.describe("[AUTH][Passkey][Subuser]", () => { test("should display passkey login button on subuser login page when supported", async ({ page }) => { await prepareSubuserPasskeyPage(page); const passkeyButton = page.locator(SUBUSER_PASSKEY_BUTTON); const isVisible = await passkeyButton.isVisible(); if (isVisible) { await expect(passkeyButton).toBeVisible(); } }); test("should not disable phone login when passkey is available", async ({ page }) => { await prepareSubuserPasskeyPage(page); await expect(page.locator('input[name="phone"]')).toBeVisible(); await expect(page.locator('input[name="password"]')).toBeVisible(); await expect(page.locator("#subuser-login-button")).toBeVisible(); await expect(page.locator("#subuser-login-button")).not.toBeDisabled(); }); }); // Passkey Browser Support Tests test.describe("[AUTH][Passkey][Browser Support]", () => { test("should gracefully handle browsers without passkey support", async ({ page, browserName }) => { await prepareUserPasskeyPage(page); const passkeyButton = page.locator(USER_PASSKEY_BUTTON); const isVisible = await passkeyButton.isVisible(); console.log(`Passkey button visible in ${browserName}: ${isVisible}`); await expect(page.locator("form")).toBeVisible(); }); test("should always show password-based login as fallback", async ({ page }) => { await prepareUserPasskeyPage(page); await expect(page.getByTestId("login-customer-number")).toBeVisible(); await expect(page.getByTestId("login-password")).toBeVisible(); await expect(page.getByTestId("login-submit")).toBeVisible(); }); }); // Passkey Button State Tests test.describe("[AUTH][Passkey][Button State]", () => { test("should disable passkey button while loading", async ({ page }) => { await prepareUserPasskeyPage(page); await stubPendingPasskeyFlow(page); const passkeyButton = page.locator(USER_PASSKEY_BUTTON); if (!(await passkeyButton.isVisible())) { test.skip(); return; } await expect(passkeyButton).not.toBeDisabled(); await passkeyButton.click(); await expect(passkeyButton).toBeDisabled(); }); });