da331121f21f

test: prove passkey create+get end-to-end — extend compile-stripped E2E Pro bypass to provider-enable and get gates, drive smoke setup over messages (create/unlock/enable, lockOnClose off), approve in fresh popups; smoke now green (v3.6.140)

time
2026-05-30 20:44 UTC · 4 months ago
machine
koh2
agent
claude-opus-4-8
session
fix passkey document-is-not-defined
parent
071b1347e42d
3 files src/background/messages.ts+8 −0src/background/passkey.ts+1 −1tests/passkeys.spec.ts+33 −18
src/background/messages.ts+8 −0
@@ -65,6 +65,13 @@6565   upsertPairedDevice as upsertPairedDeviceV3,6666 } from "./paired-devices";6767 68+// Compile-time E2E flag, folded to a literal by Vite's `define` (false in69+// every production build — verified in the bundled output). Mirrors the70+// create-path bypass in passkey.ts so the Playwright passkey smoke can enable71+// the provider without a real Pro licence; the gate is fully intact for users.72+declare const __PEACH_E2E_PASSKEYS__: boolean;73+const PASSKEY_E2E_PRO_BYPASS = __PEACH_E2E_PASSKEYS__ === true;74+6875 let nativeSyncRunnerBootstrapped = false;6976 7077 function unhexBytes(hex: string): Uint8Array {@@ -86,6 +93,7 @@8693   feature: ProFeature,8794   trigger: "user" | "background" = "user"8895 ): Promise<{ allowed: boolean }> {96+  if (PASSKEY_E2E_PRO_BYPASS && feature === "passkeys") return { allowed: true };8997   if (await isProUser()) return { allowed: true };9098 9199   if (trigger === "user") {

view file

src/background/passkey.ts+1 −1
@@ -655,7 +655,7 @@655655     // Vault is now unlocked — fall through to authenticate.656656   }657657 658-  if (!(await isProUser())) {658+  if (!PASSKEY_CREATE_E2E_BYPASS && !(await isProUser())) {659659     await rejectGet(request.requestId, "NotAllowedError", "Passkey use requires Peach Pro.");660660     broadcastPasskeyProGate();661661     return;

view file

tests/passkeys.spec.ts+33 −18
@@ -70,31 +70,46 @@7070 }7171 7272 async function createVaultAndEnablePasskeys(popup: Page) {73-  const passwordInput = popup.locator("#master-password");74-  if (await passwordInput.isVisible().catch(() => false)) {75-    await passwordInput.fill("passkeysmoke123");76-    await popup.locator("#confirm-password").fill("passkeysmoke123");77-    await popup.getByRole("button", { name: /create vault/i }).click();78-    await popup.waitForTimeout(1500);79-  }80-81-  const response = await popup.evaluate(async () => {82-    return new Promise((resolve) => {83-      chrome.runtime.sendMessage(84-        { type: "SET_PASSKEY_PROVIDER_ENABLED", payload: { enabled: true } },85-        (result) => resolve(result)86-      );73+  // Drive setup over messages in a single service-worker activation: create the74+  // vault, UNLOCK (which persists the session key so the proxy sees an unlocked75+  // vault on its next wake), enable the provider, and disable lock-on-close so76+  // the approval popup's lifecycle can't lock the vault mid-ceremony. The old77+  // UI-click setup was flaky because createVault alone does not persist a78+  // session key, so a respawned worker handled the ceremony locked.79+  const result = await popup.evaluate(async () => {80+    const send = (m: object) =>81+      new Promise<{ ok?: boolean }>((res) => chrome.runtime.sendMessage(m, res));82+    const create = await send({83+      type: "CREATE_VAULT",84+      payload: { password: "passkeysmoke123" },8785     });86+    const unlock = await send({87+      type: "UNLOCK",88+      payload: { password: "passkeysmoke123" },89+    });90+    await send({ type: "UPDATE_SETTINGS", payload: { lockOnClose: false } });91+    const enable = await send({92+      type: "SET_PASSKEY_PROVIDER_ENABLED",93+      payload: { enabled: true },94+    });95+    return { create, unlock, enable };8896   });8997 90-  expect((response as { ok?: boolean }).ok).toBe(true);98+  expect(result.create.ok).toBe(true);99+  expect(result.unlock.ok).toBe(true);100+  expect(result.enable.ok).toBe(true);91101 }92102 93103 async function approvePopupRequest(94-  popup: Page,104+  ctx: BrowserContext,105+  extId: string,95106   buttonName: RegExp,96107   expectedTitle: RegExp97108 ) {109+  // Open a fresh popup to catch the pending-request approval surface. The popup110+  // is left open (closed at context teardown); lock-on-close is disabled during111+  // setup so its eventual disconnect cannot lock the vault or deny the request.112+  const popup = await openPopup(ctx, extId);98113   await expect(popup.getByRole("heading", { name: expectedTitle })).toBeVisible({99114     timeout: 15_000,100115   });@@ -125,7 +140,7 @@125140     await page.goto(url);126141 127142     await page.getByRole("button", { name: /create passkey/i }).click();128-    await approvePopupRequest(popup, /save passkey/i, /save a passkey\?/i);143+    await approvePopupRequest(ctx, extId, /save passkey/i, /save a passkey\?/i);129144 130145     const createStatus = page.locator("#create-status");131146     await expect(createStatus).toHaveAttribute("data-state", "ok", {@@ -134,7 +149,7 @@134149     await expect(createStatus).toContainText("Created");135150 136151     await page.getByRole("button", { name: /authenticate/i }).click();137-    await approvePopupRequest(popup, /sign in/i, /sign in with passkey\?/i);152+    await approvePopupRequest(ctx, extId, /sign in/i, /sign in with passkey\?/i);138153 139154     const getStatus = page.locator("#get-status");140155     await expect(getStatus).toHaveAttribute("data-state", "ok", {

view file