48 lines
1.4 KiB
TypeScript
48 lines
1.4 KiB
TypeScript
import { expect } from "@playwright/test";
|
|
import { test } from "../fixtures";
|
|
import { registerAndLogin } from "../helpers";
|
|
|
|
/**
|
|
* Enables a virtual WebAuthn authenticator on the given context so the browser
|
|
* can complete passkey ceremonies without a real device.
|
|
*/
|
|
async function enableVirtualAuthenticator(context: any) {
|
|
const cdp = await context.newCDPSession(context.pages()[0]);
|
|
await cdp.send("WebAuthn.enable", { enableUI: false });
|
|
await cdp.send("WebAuthn.addVirtualAuthenticator", {
|
|
options: {
|
|
protocol: "ctap2",
|
|
transport: "internal",
|
|
hasResidentKey: true,
|
|
hasUserVerification: true,
|
|
isUserVerified: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
test("a user can register a passkey and sign in with it", async ({ page, browser, server }) => {
|
|
const context = await browser.newContext({ baseURL: server.baseURL });
|
|
const p = await context.newPage();
|
|
|
|
await enableVirtualAuthenticator(context);
|
|
|
|
// Register with a password first.
|
|
await registerAndLogin(p, "alice@example.com");
|
|
|
|
// Add a passkey from the account page.
|
|
await p.goto("/account");
|
|
await p.click("#add-passkey");
|
|
await expect(p.locator(".passkey-row")).toHaveCount(1);
|
|
|
|
// Log out.
|
|
await p.click('button:has-text("Sign out")');
|
|
await expect(p).toHaveURL(/\/login/);
|
|
|
|
// Sign in with the passkey.
|
|
await p.fill("#email", "alice@example.com");
|
|
await p.click("#passkey-login");
|
|
await expect(p).toHaveURL(/\/lists/);
|
|
|
|
await context.close();
|
|
});
|