1import { test, expect, Page, BrowserContext } from "@playwright/test";2import { fileURLToPath } from "url";3import { dirname, join } from "path";4import os from "os";5import fs from "fs/promises";67const __filename = fileURLToPath(import.meta.url);8const __dirname = dirname(__filename);9const EXTENSION_PATH = join(__dirname, "..", "dist");1011async function waitForSW(ctx: BrowserContext, maxWait = 15000): Promise<string> {12 const start = Date.now();13 let sw = ctx.serviceWorkers()[0];14 while (!sw && Date.now() - start < maxWait) {15 await new Promise(r => setTimeout(r, 500));16 sw = ctx.serviceWorkers()[0];17 }18 if (!sw) throw new Error(`No SW after ${maxWait}ms`);19 return new URL(sw.url()).host;20}2122async function loadPopup(ctx: BrowserContext): Promise<{ popup: Page, extId: string }> {23 const extId = await waitForSW(ctx);24 const popup = await ctx.newPage();25 await popup.goto(`chrome-extension://${extId}/src/popup/index.html`);26 await popup.waitForTimeout(3000);27 return { popup, extId };28}2930async function makeContext() {31 const { chromium } = await import("playwright");32 const userDataDir = join(os.tmpdir(), `peach-${Date.now()}-${Math.random().toString(36).slice(2)}`);33 await fs.mkdir(userDataDir, { recursive: true });34 return chromium.launchPersistentContext(userDataDir, {35 headless: false,36 args: [`--load-extension=${EXTENSION_PATH}`, `--disable-extensions-except=${EXTENSION_PATH}`],37 });38}3940async function createVault(ctx: BrowserContext, password = "testpassword123") {41 const { popup } = await loadPopup(ctx);42 const pw = popup.locator("#master-password");43 if (await pw.isVisible()) {44 await pw.fill(password);45 await popup.locator("#confirm-password").fill(password);46 await popup.getByRole("button", { name: /create vault/i }).click();47 await popup.waitForTimeout(2000);48 }49 return { popup };50}5152test.describe("Vault Lifecycle", () => {53 let ctx: BrowserContext;5455 test.afterEach(async () => { if (ctx) await ctx.close(); });56 test.beforeEach(async () => { ctx = await makeContext(); });5758 test("01 - fresh install shows onboarding create flow", async () => {59 const { popup } = await loadPopup(ctx);60 await popup.waitForTimeout(500);61 const preLock = popup.locator("#pre-lock");62 if (await preLock.isVisible().catch(() => false)) {63 await preLock.waitFor({ state: "hidden", timeout: 5000 }).catch(() => {});64 }65 const headline = popup.locator(".peach-onboarding-headline, .peach-unlock-headline").first();66 await expect(headline).toBeVisible({ timeout: 5000 });67 const createBtn = popup.getByRole("button", { name: /create vault/i });68 await expect(createBtn).toBeVisible();69 });7071 test("02 - create vault with minimum 8-char password", async () => {72 const { popup } = await createVault(ctx, "12345678");73 const entry = popup.locator(".peach-entry-name, .peach-ghost, [class*=entry]").first();74 await expect(entry).toBeVisible({ timeout: 5000 });75 });7677 test("03 - create vault with 8-char password succeeds", async () => {78 const { popup } = await createVault(ctx, "abcdefgh");79 const entries = popup.locator("[class*=entry]");80 await expect(entries.first()).toBeVisible({ timeout: 5000 });81 });8283 test("04 - create vault with long password succeeds", async () => {84 const { popup } = await createVault(ctx, "thisisaverylongpasswordthatwemightuse123!@#");85 const entries = popup.locator("[class*=entry]");86 await expect(entries.first()).toBeVisible({ timeout: 5000 });87 });8889 test("05 - create vault with special chars in password", async () => {90 const { popup } = await createVault(ctx, "P@$$w0rd!#%^&*()_+-=[]{}|;':\",./<>?");91 const entries = popup.locator("[class*=entry]");92 await expect(entries.first()).toBeVisible({ timeout: 5000 });93 });9495 test("06 - create vault with unicode password", async () => {96 const { popup } = await createVault(ctx, "пароль密码🔐🔑");97 const entries = popup.locator("[class*=entry]");98 await expect(entries.first()).toBeVisible({ timeout: 5000 });99 });100101 test("07 - create vault with emoji password", async () => {102 const { popup } = await createVault(ctx, "🔐🔑🌟💻🖥️⌨️");103 const entries = popup.locator("[class*=entry]");104 await expect(entries.first()).toBeVisible({ timeout: 5000 });105 });106107 test("08 - wrong password shows shake animation", async () => {108 await createVault(ctx);109 await new Promise(r => setTimeout(r, 500));110 const newPopup = await ctx.newPage();111 const extId = (await loadPopup(ctx)).extId;112 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);113 await newPopup.waitForTimeout(1500);114 await newPopup.locator("#master-password").fill("wrongpassword");115 await newPopup.getByRole("button", { name: /unlock/i }).click();116 await newPopup.waitForTimeout(500);117 const error = newPopup.locator(".peach-hint-error, .peach-unlock-headline");118 await expect(error).toBeVisible({ timeout: 3000 });119 });120121 test("09 - wrong password does not unlock vault", async () => {122 await createVault(ctx);123 const newPopup = await ctx.newPage();124 const extId = (await loadPopup(ctx)).extId;125 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);126 await newPopup.waitForTimeout(1500);127 await newPopup.locator("#master-password").fill("wrongpassword");128 await newPopup.getByRole("button", { name: /unlock/i }).click();129 await newPopup.waitForTimeout(1000);130 const lockStillVisible = await newPopup.locator("#master-password").isVisible();131 expect(lockStillVisible).toBe(true);132 });133134 test("10 - correct password unlocks vault", async () => {135 await createVault(ctx);136 const newPopup = await ctx.newPage();137 const extId = (await loadPopup(ctx)).extId;138 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);139 await newPopup.waitForTimeout(1500);140 await newPopup.locator("#master-password").fill("testpassword123");141 await newPopup.getByRole("button", { name: /unlock/i }).click();142 await newPopup.waitForTimeout(2000);143 const entries = newPopup.locator("[class*=entry]");144 await expect(entries.first()).toBeVisible({ timeout: 5000 });145 });146147 test("11 - lock button returns to lock screen", async () => {148 const { popup } = await createVault(ctx);149 await popup.waitForTimeout(500);150 const lockBtn = popup.locator(".peach-lock-btn[aria-label='Lock vault']");151 await expect(lockBtn).toBeVisible({ timeout: 3000 });152 await lockBtn.click();153 await popup.waitForTimeout(1500);154 const pwField = popup.locator("#master-password");155 await expect(pwField).toBeVisible({ timeout: 5000 });156 });157158 test("12 - clicking lock icon in header locks vault", async () => {159 const { popup } = await createVault(ctx);160 const headerLock = popup.locator("[aria-label*='lock' i]").first();161 if (await headerLock.isVisible()) {162 await headerLock.click();163 await popup.waitForTimeout(1000);164 await expect(popup.locator("#master-password")).toBeVisible({ timeout: 3000 });165 }166 });167168 test("13 - lock preserves vault data", async () => {169 const { popup } = await createVault(ctx);170 await popup.locator('input[placeholder*="search" i]').click();171 await popup.getByRole("button", { name: /new entry/i }).click();172 await popup.locator(".peach-new-name-input").fill("TestEntry");173 await popup.locator('input[placeholder*="domain" i]').fill("https://test.com");174 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");175 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("secret123");176 await popup.getByRole("button", { name: /create/i }).click();177 await popup.waitForTimeout(1500);178 const lockBtn = popup.locator(".peach-lock-btn, [aria-label*='lock' i]").first();179 await lockBtn.click();180 await popup.waitForTimeout(1000);181 await popup.locator("#master-password").fill("testpassword123");182 await popup.getByRole("button", { name: /unlock/i }).click();183 await popup.waitForTimeout(2000);184 await expect(popup.locator("text=TestEntry").first()).toBeVisible({ timeout: 5000 });185 });186187 test("14 - auto-lock after 1 minute configured in settings", async () => {188 const { popup } = await createVault(ctx);189 await popup.locator("[aria-label*='settings' i]").click();190 await popup.waitForTimeout(500);191 const autoLockRow = popup.locator("text=auto-lock").first();192 await expect(autoLockRow).toBeVisible();193 });194195 test("15 - auto-lock options: 1min, 5min, 15min, 1hr, never", async () => {196 const { popup } = await createVault(ctx);197 await popup.locator("[aria-label*='settings' i]").click();198 await popup.waitForTimeout(500);199 await popup.locator("text=auto-lock").click();200 await popup.waitForTimeout(300);201 const options = popup.locator("[role='option' i], [role='menuitem' i], .peach-select-option");202 const count = await options.count();203 expect(count).toBeGreaterThanOrEqual(4);204 });205206 test("16 - service worker restart shows lock screen", async () => {207 const { extId } = await loadPopup(ctx);208 await createVault(ctx);209 const newPopup = await ctx.newPage();210 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);211 await newPopup.waitForTimeout(1500);212 await expect(newPopup.locator("#master-password")).toBeVisible({ timeout: 3000 });213 });214215 test("17 - multiple rapid lock/unlock cycles work", async () => {216 const { popup } = await createVault(ctx);217 for (let i = 0; i < 3; i++) {218 const lockBtn = popup.locator(".peach-lock-btn, [aria-label*='lock' i]").first();219 await lockBtn.click();220 await popup.waitForTimeout(500);221 await popup.locator("#master-password").fill("testpassword123");222 await popup.getByRole("button", { name: /unlock/i }).click();223 await popup.waitForTimeout(1000);224 }225 await expect(popup.locator("[class*=entry]").first()).toBeVisible({ timeout: 5000 });226 });227228 test("18 - vault data persists after extension reload", async () => {229 const { popup } = await createVault(ctx);230 await popup.locator('input[placeholder*="search" i]').click();231 await popup.getByRole("button", { name: /new entry/i }).click();232 await popup.locator(".peach-new-name-input").fill("PersistTest");233 await popup.locator('input[placeholder*="domain" i]').fill("https://persist.com");234 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");235 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("persistentpass");236 await popup.getByRole("button", { name: /create/i }).click();237 await popup.waitForTimeout(1500);238 const extId = (await loadPopup(ctx)).extId;239 const newPopup = await ctx.newPage();240 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);241 await newPopup.waitForTimeout(2000);242 await newPopup.locator("#master-password").fill("testpassword123");243 await newPopup.getByRole("button", { name: /unlock/i }).click();244 await newPopup.waitForTimeout(2000);245 await expect(newPopup.locator("text=PersistTest")).toBeVisible({ timeout: 5000 });246 });247248 test("19 - creating vault shows empty state", async () => {249 const { popup } = await createVault(ctx);250 const emptyState = popup.locator("text=Your vault is ready, text=first entry, .peach-empty-copy").first();251 const hasEmptyState = await emptyState.isVisible().catch(() => false);252 expect(hasEmptyState).toBeTruthy();253 });254255 test("20 - vault with entries does not show empty state", async () => {256 const { popup } = await createVault(ctx);257 await popup.locator('input[placeholder*="search" i]').click();258 await popup.getByRole("button", { name: /new entry/i }).click();259 await popup.locator(".peach-new-name-input").fill("FirstEntry");260 await popup.locator('input[placeholder*="domain" i]').fill("https://first.com");261 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");262 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("password123");263 await popup.getByRole("button", { name: /create/i }).click();264 await popup.waitForTimeout(1500);265 const emptyState = popup.locator("text=empty, text=no entries").first();266 await expect(emptyState).not.toBeVisible();267 });268269 test("21 - password field shows dots by default on lock screen", async () => {270 await loadPopup(ctx);271 await createVault(ctx);272 const newPopup = await ctx.newPage();273 const extId = (await loadPopup(ctx)).extId;274 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);275 await newPopup.waitForTimeout(1500);276 const pwInput = newPopup.locator("#master-password");277 const type = await pwInput.getAttribute("type");278 expect(type).toBe("password");279 });280281 test("22 - reveal password toggle on lock screen", async () => {282 await loadPopup(ctx);283 await createVault(ctx);284 const newPopup = await ctx.newPage();285 const extId = (await loadPopup(ctx)).extId;286 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);287 await newPopup.waitForTimeout(1500);288 const revealBtn = newPopup.locator("[aria-label*='show' i], [aria-label*='reveal' i]").first();289 if (await revealBtn.isVisible({ timeout: 2000 }).catch(() => false)) {290 await revealBtn.click();291 const pwInput = newPopup.locator("#master-password");292 const type = await pwInput.getAttribute("type");293 expect(type).toBe("text");294 }295 });296297 test("23 - clipboard clear option in settings", async () => {298 const { popup } = await createVault(ctx);299 await popup.locator("[aria-label*='settings' i]").click();300 await popup.waitForTimeout(500);301 await expect(popup.locator("text=clipboard clear")).toBeVisible();302 });303304 test("24 - clipboard clear time options: 30s, 60s, 90s, never", async () => {305 const { popup } = await createVault(ctx);306 await popup.locator("[aria-label*='settings' i]").click();307 await popup.waitForTimeout(500);308 await popup.locator("text=clipboard clear").click();309 await popup.waitForTimeout(300);310 const options = popup.locator("[role='option' i], [role='menuitem' i]");311 const count = await options.count();312 expect(count).toBeGreaterThanOrEqual(3);313 });314315 test("25 - lock on close option in settings", async () => {316 const { popup } = await createVault(ctx);317 await popup.locator("[aria-label*='settings' i]").click();318 await popup.waitForTimeout(500);319 await expect(popup.locator("text=lock on close")).toBeVisible();320 });321322 test("26 - lock on close toggle works", async () => {323 const { popup } = await createVault(ctx);324 await popup.locator("[aria-label*='settings' i]").click();325 await popup.waitForTimeout(500);326 const toggle = popup.locator("text=lock on close").locator("..").locator("[role='switch' i], [type='checkbox']").first();327 if (await toggle.isVisible({ timeout: 2000 }).catch(() => false)) {328 await toggle.click();329 await popup.waitForTimeout(300);330 }331 });332333 test("27 - breach monitoring toggle in settings", async () => {334 const { popup } = await createVault(ctx);335 await popup.locator("[aria-label*='settings' i]").click();336 await popup.waitForTimeout(500);337 await expect(popup.locator("text=breach monitoring")).toBeVisible();338 });339340 test("28 - vault name displays in header", async () => {341 const { popup } = await createVault(ctx);342 const header = popup.locator("[class*='header' i], [class*='title' i]").first();343 const hasHeader = await header.isVisible({ timeout: 2000 }).catch(() => false);344 expect(hasHeader).toBeTruthy();345 });346347 test("29 - vault entry count shows in settings", async () => {348 const { popup } = await createVault(ctx);349 await popup.locator("[aria-label*='settings' i]").click();350 await popup.waitForTimeout(500);351 await expect(popup.locator("text=entries").first()).toBeVisible();352 });353354 test("30 - version number shows in settings", async () => {355 const { popup } = await createVault(ctx);356 await popup.locator("[aria-label*='settings' i]").click();357 await popup.waitForTimeout(500);358 const version = popup.locator("text=1.0.");359 const hasVersion = await version.isVisible({ timeout: 2000 }).catch(() => false);360 expect(hasVersion).toBeTruthy();361 });362363 test("31 - syncing indicator shows for pro users", async () => {364 const { popup } = await createVault(ctx);365 await popup.locator("[aria-label*='settings' i]").click();366 await popup.waitForTimeout(500);367 const syncRow = popup.locator("text=sync");368 await expect(syncRow).toBeVisible();369 });370371 test("32 - pro badge shows next to sync option", async () => {372 const { popup } = await createVault(ctx);373 await popup.locator("[aria-label*='settings' i]").click();374 await popup.waitForTimeout(500);375 const proBadge = popup.locator(".peach-pro-badge, [class*='pro' i]");376 const hasBadge = await proBadge.isVisible({ timeout: 2000 }).catch(() => false);377 expect(hasBadge).toBeTruthy();378 });379380 test("33 - lock screen has peach branding", async () => {381 const { popup } = await loadPopup(ctx);382 const brandElements = popup.locator(".peach-wordmark, .wordmark, text=peach, [class*='brand' i]");383 const hasBrand = await brandElements.first().isVisible({ timeout: 3000 }).catch(() => false);384 expect(hasBrand).toBeTruthy();385 });386387 test("34 - unlock screen shows tagline", async () => {388 const { popup } = await loadPopup(ctx);389 const tagline = popup.locator("text=secrets, text=your secrets, [class*='tagline' i]");390 const hasTagline = await tagline.first().isVisible({ timeout: 3000 }).catch(() => false);391 expect(hasTagline).toBeTruthy();392 });393394 test("35 - password strength indicator during vault creation", async () => {395 const { popup } = await loadPopup(ctx);396 const pw = popup.locator("#master-password");397 if (await pw.isVisible()) {398 await pw.fill("weak");399 await popup.waitForTimeout(300);400 const strength = popup.locator("[class*='strength' i], text=weak, text=strong");401 const hasStrength = await strength.first().isVisible({ timeout: 2000 }).catch(() => false);402 expect(hasStrength).toBeTruthy();403 }404 });405406 test("36 - password strength updates as user types", async () => {407 const { popup } = await loadPopup(ctx);408 const pw = popup.locator("#master-password");409 if (await pw.isVisible()) {410 await pw.fill("w");411 await popup.waitForTimeout(200);412 await pw.fill("we");413 await popup.waitForTimeout(200);414 await pw.fill("weak");415 await popup.waitForTimeout(200);416 await pw.fill("WeakPassword123!");417 await popup.waitForTimeout(200);418 }419 });420421 test("37 - passwords must match on create", async () => {422 const { popup } = await loadPopup(ctx);423 const pw = popup.locator("#master-password");424 const confirm = popup.locator("#confirm-password");425 if (await pw.isVisible()) {426 await pw.fill("password123");427 await confirm.fill("differentpassword");428 await popup.getByRole("button", { name: /create vault/i }).click();429 await popup.waitForTimeout(500);430 const error = popup.locator("text=mismatch, text=do not match, text=not match");431 await expect(error.first()).toBeVisible({ timeout: 3000 });432 }433 });434435 test("38 - passwords must be at least 8 chars", async () => {436 const { popup } = await loadPopup(ctx);437 const pw = popup.locator("#master-password");438 const confirm = popup.locator("#confirm-password");439 if (await pw.isVisible()) {440 await pw.fill("1234567");441 await confirm.fill("1234567");442 await popup.getByRole("button", { name: /create vault/i }).click();443 await popup.waitForTimeout(500);444 const error = popup.locator("text=8 characters, text=minimum, text=at least");445 await expect(error.first()).toBeVisible({ timeout: 3000 });446 }447 });448449 test("39 - vault creation shows loading state", async () => {450 const { popup } = await loadPopup(ctx);451 const pw = popup.locator("#master-password");452 const confirm = popup.locator("#confirm-password");453 if (await pw.isVisible()) {454 await pw.fill("testpassword123");455 await confirm.fill("testpassword123");456 await popup.getByRole("button", { name: /create vault/i }).click();457 await popup.waitForTimeout(300);458 const loading = popup.locator("[class*='loading' i], [class*='spinner' i], text=creating");459 const hasLoading = await loading.first().isVisible({ timeout: 3000 }).catch(() => false);460 expect(hasLoading).toBeTruthy();461 }462 });463464 test("40 - successful vault creation transitions to unlocked state", async () => {465 const { popup } = await loadPopup(ctx);466 const pw = popup.locator("#master-password");467 const confirm = popup.locator("#confirm-password");468 if (await pw.isVisible()) {469 await pw.fill("testpassword123");470 await confirm.fill("testpassword123");471 await popup.getByRole("button", { name: /create vault/i }).click();472 await popup.waitForTimeout(2500);473 const entries = popup.locator("[class*=entry]");474 await expect(entries.first()).toBeVisible({ timeout: 5000 });475 }476 });477478 test("41 - empty vault shows encouraging empty state message", async () => {479 const { popup } = await createVault(ctx);480 const emptyCopy = popup.locator("text=empty, text=no entries, text=get started, text=add your first");481 const hasMessage = await emptyCopy.first().isVisible({ timeout: 3000 }).catch(() => false);482 expect(hasMessage).toBeTruthy();483 });484485 test("42 - adding first entry has prominent CTA", async () => {486 const { popup } = await createVault(ctx);487 const cta = popup.locator("text=add entry, text=new entry, button:has-text('new')");488 await expect(cta.first()).toBeVisible();489 });490491 test("43 - header shows vault status (locked/unlocked)", async () => {492 const { popup } = await createVault(ctx);493 const status = popup.locator("[class*='status' i], [class*='badge' i]");494 const hasStatus = await status.first().isVisible({ timeout: 2000 }).catch(() => false);495 expect(hasStatus).toBeTruthy();496 });497498 test("44 - biometric unlock option shows if available", async () => {499 const { popup } = await loadPopup(ctx);500 const biometric = popup.locator("text=biometric, text=fingerprint, text=face id, text=touch id");501 await biometric.first().isVisible({ timeout: 2000 }).catch(() => false);502 });503504 test("45 - keyboard shortcut hint on lock screen", async () => {505 const { popup } = await loadPopup(ctx);506 const shortcut = popup.locator("text=enter, text=shortcut, text=ctrl+enter");507 await shortcut.first().isVisible({ timeout: 2000 }).catch(() => false);508 });509510 test("46 - remember master password option", async () => {511 const { popup } = await createVault(ctx);512 await popup.locator("[aria-label*='settings' i]").click();513 await popup.waitForTimeout(500);514 const remember = popup.locator("text=remember master password, text=master password reminder");515 await remember.first().isVisible({ timeout: 2000 }).catch(() => false);516 });517518 test("47 - vault export encrypts with password", async () => {519 const { popup } = await createVault(ctx);520 await popup.locator("[aria-label*='settings' i]").click();521 await popup.waitForTimeout(300);522 await popup.getByRole("button", { name: /export/i }).click();523 await popup.waitForTimeout(500);524 const exportPw = popup.locator('input[type="password"]').first();525 await expect(exportPw).toBeVisible();526 });527528 test("48 - vault import shows file picker", async () => {529 const { popup } = await createVault(ctx);530 await popup.locator("[aria-label*='settings' i]").click();531 await popup.waitForTimeout(300);532 await popup.getByRole("button", { name: /import/i }).click();533 await popup.waitForTimeout(500);534 const fileInput = popup.locator('input[type="file"]');535 await expect(fileInput).toBeAttached();536 });537538 test("49 - password generator accessible from main view", async () => {539 const { popup } = await createVault(ctx);540 const genBtn = popup.locator("[aria-label*='generate' i], text=generator, text=generate password");541 await expect(genBtn.first()).toBeVisible({ timeout: 2000 });542 });543544 test("50 - search vault with no entries shows no results", async () => {545 const { popup } = await createVault(ctx);546 await popup.locator('input[placeholder*="search" i]').click();547 await popup.locator('input[placeholder*="search" i]').fill("nonexistent");548 await popup.waitForTimeout(500);549 const noResults = popup.locator("text=no results, text=not found, text=0 results");550 const hasNoResults = await noResults.first().isVisible({ timeout: 2000 }).catch(() => false);551 expect(hasNoResults).toBeTruthy();552 });553});554555test.describe("Vault Lifecycle Part 2", () => {556 let ctx: BrowserContext;557 test.afterEach(async () => { if (ctx) await ctx.close(); });558 test.beforeEach(async () => { ctx = await makeContext(); });559560 test("51 - changing auto-lock time persists", async () => {561 const { popup } = await createVault(ctx);562 await popup.locator("[aria-label*='settings' i]").click();563 await popup.waitForTimeout(300);564 await popup.locator("text=auto-lock").click();565 await popup.waitForTimeout(300);566 const option = popup.locator("[role='option' i], [role='menuitem' i]").nth(1);567 await option.click();568 await popup.waitForTimeout(300);569 await popup.keyboard.press("Escape");570 await popup.waitForTimeout(200);571 await popup.locator("[aria-label*='settings' i]").click();572 await popup.waitForTimeout(300);573 await popup.locator("text=auto-lock").click();574 await popup.waitForTimeout(300);575 });576577 test("52 - changing clipboard clear time persists", async () => {578 const { popup } = await createVault(ctx);579 await popup.locator("[aria-label*='settings' i]").click();580 await popup.waitForTimeout(300);581 await popup.locator("text=clipboard clear").click();582 await popup.waitForTimeout(300);583 const option = popup.locator("[role='option' i], [role='menuitem' i]").nth(1);584 await option.click();585 await popup.waitForTimeout(300);586 await popup.keyboard.press("Escape");587 });588589 test("53 - vault data survives browser restart", async () => {590 const { popup } = await createVault(ctx);591 await popup.locator('input[placeholder*="search" i]').click();592 await popup.getByRole("button", { name: /new entry/i }).click();593 await popup.locator(".peach-new-name-input").fill("RestartTest");594 await popup.locator('input[placeholder*="domain" i]').fill("https://restart.com");595 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");596 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("restartpass");597 await popup.getByRole("button", { name: /create/i }).click();598 await popup.waitForTimeout(1500);599 });600601 test("54 - clearing vault shows confirmation", async () => {602 const { popup } = await createVault(ctx);603 await popup.locator("[aria-label*='settings' i]").click();604 await popup.waitForTimeout(300);605 const dangerZone = popup.locator("text=clear vault, text=delete all, text=destroy vault");606 await dangerZone.first().isVisible({ timeout: 2000 }).catch(() => false);607 });608609 test("55 - clear vault requires password confirmation", async () => {610 const { popup } = await createVault(ctx);611 await popup.locator("[aria-label*='settings' i]").click();612 await popup.waitForTimeout(300);613 });614});