import { test, expect, type BrowserContext } from "@playwright/test"; import { UNLOCKED, VAULT_PASSWORD, addEntry, loadPopup, lockVault, makeContext, setupVault, unlockVault, } from "./helpers/vault"; // Every test creates a real vault (onboarding + Argon2id KDF + recovery // confirmation ≈ 12s) and several unlock again, which runs Argon2 a second // time. The global 30s cap is too tight for those. test.describe.configure({ timeout: 90_000 }); // This file used to hold 55 tests written against a UI that no longer exists // (an 8-character password minimum, a "1.0." version string, a pro badge, a // settings sheet reached by aria-label='settings'). None of them ever ran // against a real vault, because the setup helper looked for #master-password // while the onboarding carousel was still on screen and silently skipped // vault creation. What is left is the part that is both real and not already // covered by lock-screen.spec.ts: the create-vault gate, and persistence. /** Advance the intro carousel until the create-vault form is on screen. */ async function reachCreateScreen(popup: import("@playwright/test").Page) { const pw = popup.locator("#master-password"); for (let i = 0; i < 5 && !(await pw.isVisible().catch(() => false)); i++) { await popup.locator(".peach-onboarding-card").click(); await popup.waitForTimeout(300); } await expect(pw).toBeVisible(); } test.describe("Vault creation gate", () => { let ctx: BrowserContext; test.afterEach(async () => { if (ctx) await ctx.close(); }); test.beforeEach(async () => { ctx = await makeContext("peach-lifecycle"); }); test("VL01 - fresh install opens the intro carousel", async () => { const { popup } = await loadPopup(ctx); await expect(popup.locator(".peach-onboarding-card")).toBeVisible({ timeout: 5000 }); await expect(popup.locator("#master-password")).toHaveCount(0); }); test("VL02 - the carousel leads to the create-vault form", async () => { const { popup } = await loadPopup(ctx); await reachCreateScreen(popup); await expect(popup.locator("#confirm-password")).toBeVisible(); await expect(popup.locator(".peach-newvault-submit")).toBeDisabled(); }); test("VL03 - a password under 12 characters blocks creation", async () => { const { popup } = await loadPopup(ctx); await reachCreateScreen(popup); await popup.locator("#master-password").fill("shortpw12"); await popup.locator("#confirm-password").fill("shortpw12"); const submit = popup.locator(".peach-newvault-submit"); await expect(submit).toBeDisabled(); await expect(submit).toHaveText(/more characters/i); }); test("VL04 - a long but weak password blocks creation", async () => { const { popup } = await loadPopup(ctx); await reachCreateScreen(popup); await popup.locator("#master-password").fill("password123456"); await popup.locator("#confirm-password").fill("password123456"); const submit = popup.locator(".peach-newvault-submit"); await expect(submit).toBeDisabled(); await expect(submit).toHaveText(/too weak/i); }); test("VL05 - mismatched confirmation blocks creation", async () => { const { popup } = await loadPopup(ctx); await reachCreateScreen(popup); await popup.locator("#master-password").fill(VAULT_PASSWORD); await popup.locator("#confirm-password").fill("correct horse battery stapl"); const submit = popup.locator(".peach-newvault-submit"); await expect(submit).toBeDisabled(); await expect(submit).toHaveText(/don't match/i); }); test("VL06 - the strength meter reacts to the password", async () => { const { popup } = await loadPopup(ctx); await reachCreateScreen(popup); const label = popup.locator(".peach-strength-label"); await popup.locator("#master-password").fill("aaaa"); await expect(label).toHaveText("weak"); await popup.locator("#master-password").fill(VAULT_PASSWORD); await expect(label).toHaveText(/good|strong/); }); test("VL07 - a passing password creates the vault and unlocks it", async () => { const { popup } = await setupVault(ctx); await expect(popup.locator(UNLOCKED)).toBeVisible(); }); }); test.describe("Vault persistence", () => { let ctx: BrowserContext; test.afterEach(async () => { if (ctx) await ctx.close(); }); test.beforeEach(async () => { ctx = await makeContext("peach-lifecycle"); }); test("VL08 - a new vault shows the empty state", async () => { const { popup } = await setupVault(ctx); await expect(popup.locator(".peach-empty-copy")).toBeVisible({ timeout: 5000 }); }); test("VL09 - entries survive lock and unlock", async () => { const { popup } = await setupVault(ctx); await addEntry(popup, { title: "LockRoundTrip", url: "https://roundtrip.test", username: "u@roundtrip.test", password: "entry-secret" }); await lockVault(popup); await unlockVault(popup); await expect(popup.locator(".peach-entry-name-text").first()).toHaveText("LockRoundTrip", { timeout: 10000 }); }); test("VL10 - reopening the popup requires the password again", async () => { const { popup, extId } = await setupVault(ctx); await addEntry(popup, { title: "ReopenTest", url: "https://reopen.test", username: "u@reopen.test", password: "entry-secret" }); await popup.close(); const reopened = await ctx.newPage(); await reopened.goto(`chrome-extension://${extId}/src/popup/index.html`); await expect(reopened.locator("#unlock-password")).toBeVisible({ timeout: 15000 }); await unlockVault(reopened); await expect(reopened.locator(".peach-entry-name-text").first()).toHaveText("ReopenTest", { timeout: 10000 }); }); test("VL11 - a wrong password does not unlock a reopened popup", async () => { const { popup, extId } = await setupVault(ctx); await popup.close(); const reopened = await ctx.newPage(); await reopened.goto(`chrome-extension://${extId}/src/popup/index.html`); await expect(reopened.locator("#unlock-password")).toBeVisible({ timeout: 15000 }); await reopened.locator("#unlock-password").fill("not the right password"); await reopened.getByRole("button", { name: /unlock/i }).click(); await expect(reopened.locator(".peach-hint-error")).toBeVisible({ timeout: 15000 }); await expect(reopened.locator(UNLOCKED)).toHaveCount(0); }); });