Files
pleno-vue/tests/e2e/passkeyAuth.spec.ts
T
Jeppe Bundgaard 6c4b9cb599 Add and improve e2e tests for Edge Gateway flows and configure pre-commit hooks:
- Introduced live smoke tests for Edge Gateways, verifying gateway routes and destructive-action prevention.
- Added e2e scenarios for deep-link navigation, unavailable gateway recovery, token rotation, and background page refresh.
- Refactored test helpers for streamlined functional validation in gateway scenarios.
- Updated `EdgeGatewayTerminal.vue` with test IDs for enhanced testability.
- Added and configured Husky pre-commit hooks for automated test file formatting and validation.
2026-04-16 10:52:56 +02:00

171 lines
5.3 KiB
TypeScript

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();
});
});