1import { test, expect, type BrowserContext } from "@playwright/test";2import {3 UNLOCKED,4 VAULT_PASSWORD,5 addEntry,6 loadPopup,7 lockVault,8 makeContext,9 setupVault,10 unlockVault,11} from "./helpers/vault";1213141516test.describe.configure({ timeout: 90_000 });1718192021222324252627async function reachCreateScreen(popup: import("@playwright/test").Page) {28 const pw = popup.locator("#master-password");29 for (let i = 0; i < 5 && !(await pw.isVisible().catch(() => false)); i++) {30 await popup.locator(".peach-onboarding-card").click();31 await popup.waitForTimeout(300);32 }33 await expect(pw).toBeVisible();34}3536test.describe("Vault creation gate", () => {37 let ctx: BrowserContext;38 test.afterEach(async () => { if (ctx) await ctx.close(); });39 test.beforeEach(async () => { ctx = await makeContext("peach-lifecycle"); });4041 test("VL01 - fresh install opens the intro carousel", async () => {42 const { popup } = await loadPopup(ctx);43 await expect(popup.locator(".peach-onboarding-card")).toBeVisible({ timeout: 5000 });44 await expect(popup.locator("#master-password")).toHaveCount(0);45 });4647 test("VL02 - the carousel leads to the create-vault form", async () => {48 const { popup } = await loadPopup(ctx);49 await reachCreateScreen(popup);50 await expect(popup.locator("#confirm-password")).toBeVisible();51 await expect(popup.locator(".peach-newvault-submit")).toBeDisabled();52 });5354 test("VL03 - a password under 12 characters blocks creation", async () => {55 const { popup } = await loadPopup(ctx);56 await reachCreateScreen(popup);57 await popup.locator("#master-password").fill("shortpw12");58 await popup.locator("#confirm-password").fill("shortpw12");59 const submit = popup.locator(".peach-newvault-submit");60 await expect(submit).toBeDisabled();61 await expect(submit).toHaveText(/more characters/i);62 });6364 test("VL04 - a long but weak password blocks creation", async () => {65 const { popup } = await loadPopup(ctx);66 await reachCreateScreen(popup);67 await popup.locator("#master-password").fill("password123456");68 await popup.locator("#confirm-password").fill("password123456");69 const submit = popup.locator(".peach-newvault-submit");70 await expect(submit).toBeDisabled();71 await expect(submit).toHaveText(/too weak/i);72 });7374 test("VL05 - mismatched confirmation blocks creation", async () => {75 const { popup } = await loadPopup(ctx);76 await reachCreateScreen(popup);77 await popup.locator("#master-password").fill(VAULT_PASSWORD);78 await popup.locator("#confirm-password").fill("correct horse battery stapl");79 const submit = popup.locator(".peach-newvault-submit");80 await expect(submit).toBeDisabled();81 await expect(submit).toHaveText(/don't match/i);82 });8384 test("VL06 - the strength meter reacts to the password", async () => {85 const { popup } = await loadPopup(ctx);86 await reachCreateScreen(popup);87 const label = popup.locator(".peach-strength-label");88 await popup.locator("#master-password").fill("aaaa");89 await expect(label).toHaveText("weak");90 await popup.locator("#master-password").fill(VAULT_PASSWORD);91 await expect(label).toHaveText(/good|strong/);92 });9394 test("VL07 - a passing password creates the vault and unlocks it", async () => {95 const { popup } = await setupVault(ctx);96 await expect(popup.locator(UNLOCKED)).toBeVisible();97 });98});99100test.describe("Vault persistence", () => {101 let ctx: BrowserContext;102 test.afterEach(async () => { if (ctx) await ctx.close(); });103 test.beforeEach(async () => { ctx = await makeContext("peach-lifecycle"); });104105 test("VL08 - a new vault shows the empty state", async () => {106 const { popup } = await setupVault(ctx);107 await expect(popup.locator(".peach-empty-copy")).toBeVisible({ timeout: 5000 });108 });109110 test("VL09 - entries survive lock and unlock", async () => {111 const { popup } = await setupVault(ctx);112 await addEntry(popup, { title: "LockRoundTrip", url: "https://roundtrip.test", username: "[email protected]", password: "entry-secret" });113 await lockVault(popup);114 await unlockVault(popup);115 await expect(popup.locator(".peach-entry-name-text").first()).toHaveText("LockRoundTrip", { timeout: 10000 });116 });117118 test("VL10 - reopening the popup requires the password again", async () => {119 const { popup, extId } = await setupVault(ctx);120 await addEntry(popup, { title: "ReopenTest", url: "https://reopen.test", username: "[email protected]", password: "entry-secret" });121 await popup.close();122123 const reopened = await ctx.newPage();124 await reopened.goto(`chrome-extension://${extId}/src/popup/index.html`);125 await expect(reopened.locator("#unlock-password")).toBeVisible({ timeout: 15000 });126 await unlockVault(reopened);127 await expect(reopened.locator(".peach-entry-name-text").first()).toHaveText("ReopenTest", { timeout: 10000 });128 });129130 test("VL11 - a wrong password does not unlock a reopened popup", async () => {131 const { popup, extId } = await setupVault(ctx);132 await popup.close();133134 const reopened = await ctx.newPage();135 await reopened.goto(`chrome-extension://${extId}/src/popup/index.html`);136 await expect(reopened.locator("#unlock-password")).toBeVisible({ timeout: 15000 });137 await reopened.locator("#unlock-password").fill("not the right password");138 await reopened.getByRole("button", { name: /unlock/i }).click();139 await expect(reopened.locator(".peach-hint-error")).toBeVisible({ timeout: 15000 });140 await expect(reopened.locator(UNLOCKED)).toHaveCount(0);141 });142});