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}5152async function unlockVault(ctx: BrowserContext, password = "testpassword123") {53 const { popup } = await loadPopup(ctx);54 const pw = popup.locator("#master-password");55 if (await pw.isVisible()) {56 await pw.fill(password);57 await popup.getByRole("button", { name: /unlock/i }).click();58 await popup.waitForTimeout(1500);59 }60 return { popup };61}6263test.describe("Vault Lifecycle", () => {64 let ctx: BrowserContext;6566 test.afterEach(async () => { if (ctx) await ctx.close(); });67 test.beforeEach(async () => { ctx = await makeContext(); });6869 test("01 - fresh install shows onboarding create flow", async () => {70 const { popup } = await loadPopup(ctx);71 await popup.waitForTimeout(500);72 const preLock = popup.locator("#pre-lock");73 if (await preLock.isVisible().catch(() => false)) {74 await preLock.waitFor({ state: "hidden", timeout: 5000 }).catch(() => {});75 }76 const headline = popup.locator(".peach-onboarding-headline, .peach-unlock-headline").first();77 await expect(headline).toBeVisible({ timeout: 5000 });78 const createBtn = popup.getByRole("button", { name: /create vault/i });79 await expect(createBtn).toBeVisible();80 });8182 test("02 - create vault with minimum 8-char password", async () => {83 const { popup } = await createVault(ctx, "12345678");84 const entry = popup.locator(".peach-entry-name, .peach-ghost, [class*=entry]").first();85 await expect(entry).toBeVisible({ timeout: 5000 });86 });8788 test("03 - create vault with 8-char password succeeds", async () => {89 const { popup } = await createVault(ctx, "abcdefgh");90 const entries = popup.locator("[class*=entry]");91 await expect(entries.first()).toBeVisible({ timeout: 5000 });92 });9394 test("04 - create vault with long password succeeds", async () => {95 const { popup } = await createVault(ctx, "thisisaverylongpasswordthatwemightuse123!@#");96 const entries = popup.locator("[class*=entry]");97 await expect(entries.first()).toBeVisible({ timeout: 5000 });98 });99100 test("05 - create vault with special chars in password", async () => {101 const { popup } = await createVault(ctx, "P@$$w0rd!#%^&*()_+-=[]{}|;':\",./<>?");102 const entries = popup.locator("[class*=entry]");103 await expect(entries.first()).toBeVisible({ timeout: 5000 });104 });105106 test("06 - create vault with unicode password", async () => {107 const { popup } = await createVault(ctx, "пароль密码🔐🔑");108 const entries = popup.locator("[class*=entry]");109 await expect(entries.first()).toBeVisible({ timeout: 5000 });110 });111112 test("07 - create vault with emoji password", async () => {113 const { popup } = await createVault(ctx, "🔐🔑🌟💻🖥️⌨️");114 const entries = popup.locator("[class*=entry]");115 await expect(entries.first()).toBeVisible({ timeout: 5000 });116 });117118 test("08 - wrong password shows shake animation", async () => {119 await createVault(ctx);120 await new Promise(r => setTimeout(r, 500));121 const newPopup = await ctx.newPage();122 const extId = (await loadPopup(ctx)).extId;123 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);124 await newPopup.waitForTimeout(1500);125 await newPopup.locator("#master-password").fill("wrongpassword");126 await newPopup.getByRole("button", { name: /unlock/i }).click();127 await newPopup.waitForTimeout(500);128 const error = newPopup.locator(".peach-hint-error, .peach-unlock-headline");129 await expect(error).toBeVisible({ timeout: 3000 });130 });131132 test("09 - wrong password does not unlock vault", async () => {133 await createVault(ctx);134 const newPopup = await ctx.newPage();135 const extId = (await loadPopup(ctx)).extId;136 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);137 await newPopup.waitForTimeout(1500);138 await newPopup.locator("#master-password").fill("wrongpassword");139 await newPopup.getByRole("button", { name: /unlock/i }).click();140 await newPopup.waitForTimeout(1000);141 const lockStillVisible = await newPopup.locator("#master-password").isVisible();142 expect(lockStillVisible).toBe(true);143 });144145 test("10 - correct password unlocks vault", async () => {146 await createVault(ctx);147 const newPopup = await ctx.newPage();148 const extId = (await loadPopup(ctx)).extId;149 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);150 await newPopup.waitForTimeout(1500);151 await newPopup.locator("#master-password").fill("testpassword123");152 await newPopup.getByRole("button", { name: /unlock/i }).click();153 await newPopup.waitForTimeout(2000);154 const entries = newPopup.locator("[class*=entry]");155 await expect(entries.first()).toBeVisible({ timeout: 5000 });156 });157158 test("11 - lock button returns to lock screen", async () => {159 const { popup } = await createVault(ctx);160 await popup.waitForTimeout(500);161 const lockBtn = popup.locator(".peach-lock-btn[aria-label='Lock vault']");162 await expect(lockBtn).toBeVisible({ timeout: 3000 });163 await lockBtn.click();164 await popup.waitForTimeout(1500);165 const pwField = popup.locator("#master-password");166 await expect(pwField).toBeVisible({ timeout: 5000 });167 });168169 test("12 - clicking lock icon in header locks vault", async () => {170 const { popup } = await createVault(ctx);171 const headerLock = popup.locator("[aria-label*='lock' i]").first();172 if (await headerLock.isVisible()) {173 await headerLock.click();174 await popup.waitForTimeout(1000);175 await expect(popup.locator("#master-password")).toBeVisible({ timeout: 3000 });176 }177 });178179 test("13 - lock preserves vault data", async () => {180 const { popup } = await createVault(ctx);181 await popup.locator('input[placeholder*="search" i]').click();182 await popup.getByRole("button", { name: /new entry/i }).click();183 await popup.locator(".peach-new-name-input").fill("TestEntry");184 await popup.locator('input[placeholder*="domain" i]').fill("https://test.com");185 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");186 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("secret123");187 await popup.getByRole("button", { name: /create/i }).click();188 await popup.waitForTimeout(1500);189 const lockBtn = popup.locator(".peach-lock-btn, [aria-label*='lock' i]").first();190 await lockBtn.click();191 await popup.waitForTimeout(1000);192 await popup.locator("#master-password").fill("testpassword123");193 await popup.getByRole("button", { name: /unlock/i }).click();194 await popup.waitForTimeout(2000);195 await expect(popup.locator("text=TestEntry").first()).toBeVisible({ timeout: 5000 });196 });197198 test("14 - auto-lock after 1 minute configured in settings", async () => {199 const { popup } = await createVault(ctx);200 await popup.locator("[aria-label*='settings' i]").click();201 await popup.waitForTimeout(500);202 const autoLockRow = popup.locator("text=auto-lock").first();203 await expect(autoLockRow).toBeVisible();204 });205206 test("15 - auto-lock options: 1min, 5min, 15min, 1hr, never", async () => {207 const { popup } = await createVault(ctx);208 await popup.locator("[aria-label*='settings' i]").click();209 await popup.waitForTimeout(500);210 await popup.locator("text=auto-lock").click();211 await popup.waitForTimeout(300);212 const options = popup.locator("[role='option' i], [role='menuitem' i], .peach-select-option");213 const count = await options.count();214 expect(count).toBeGreaterThanOrEqual(4);215 });216217 test("16 - service worker restart shows lock screen", async () => {218 const { popup, extId } = await loadPopup(ctx);219 await createVault(ctx);220 const newPopup = await ctx.newPage();221 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);222 await newPopup.waitForTimeout(1500);223 await expect(newPopup.locator("#master-password")).toBeVisible({ timeout: 3000 });224 });225226 test("17 - multiple rapid lock/unlock cycles work", async () => {227 const { popup } = await createVault(ctx);228 for (let i = 0; i < 3; i++) {229 const lockBtn = popup.locator(".peach-lock-btn, [aria-label*='lock' i]").first();230 await lockBtn.click();231 await popup.waitForTimeout(500);232 await popup.locator("#master-password").fill("testpassword123");233 await popup.getByRole("button", { name: /unlock/i }).click();234 await popup.waitForTimeout(1000);235 }236 await expect(popup.locator("[class*=entry]").first()).toBeVisible({ timeout: 5000 });237 });238239 test("18 - vault data persists after extension reload", async () => {240 const { popup } = await createVault(ctx);241 await popup.locator('input[placeholder*="search" i]').click();242 await popup.getByRole("button", { name: /new entry/i }).click();243 await popup.locator(".peach-new-name-input").fill("PersistTest");244 await popup.locator('input[placeholder*="domain" i]').fill("https://persist.com");245 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");246 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("persistentpass");247 await popup.getByRole("button", { name: /create/i }).click();248 await popup.waitForTimeout(1500);249 const extId = (await loadPopup(ctx)).extId;250 const newPopup = await ctx.newPage();251 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);252 await newPopup.waitForTimeout(2000);253 await newPopup.locator("#master-password").fill("testpassword123");254 await newPopup.getByRole("button", { name: /unlock/i }).click();255 await newPopup.waitForTimeout(2000);256 await expect(newPopup.locator("text=PersistTest")).toBeVisible({ timeout: 5000 });257 });258259 test("19 - creating vault shows empty state", async () => {260 const { popup } = await createVault(ctx);261 const emptyState = popup.locator("text=Your vault is ready, text=first entry, .peach-empty-copy").first();262 const hasEmptyState = await emptyState.isVisible().catch(() => false);263 expect(hasEmptyState).toBeTruthy();264 });265266 test("20 - vault with entries does not show empty state", async () => {267 const { popup } = await createVault(ctx);268 await popup.locator('input[placeholder*="search" i]').click();269 await popup.getByRole("button", { name: /new entry/i }).click();270 await popup.locator(".peach-new-name-input").fill("FirstEntry");271 await popup.locator('input[placeholder*="domain" i]').fill("https://first.com");272 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");273 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("password123");274 await popup.getByRole("button", { name: /create/i }).click();275 await popup.waitForTimeout(1500);276 const emptyState = popup.locator("text=empty, text=no entries").first();277 await expect(emptyState).not.toBeVisible();278 });279280 test("21 - password field shows dots by default on lock screen", async () => {281 const { popup } = await loadPopup(ctx);282 await createVault(ctx);283 const newPopup = await ctx.newPage();284 const extId = (await loadPopup(ctx)).extId;285 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);286 await newPopup.waitForTimeout(1500);287 const pwInput = newPopup.locator("#master-password");288 const type = await pwInput.getAttribute("type");289 expect(type).toBe("password");290 });291292 test("22 - reveal password toggle on lock screen", async () => {293 const { popup } = await loadPopup(ctx);294 await createVault(ctx);295 const newPopup = await ctx.newPage();296 const extId = (await loadPopup(ctx)).extId;297 await newPopup.goto(`chrome-extension://${extId}/src/popup/index.html`);298 await newPopup.waitForTimeout(1500);299 const revealBtn = newPopup.locator("[aria-label*='show' i], [aria-label*='reveal' i]").first();300 if (await revealBtn.isVisible({ timeout: 2000 }).catch(() => false)) {301 await revealBtn.click();302 const pwInput = newPopup.locator("#master-password");303 const type = await pwInput.getAttribute("type");304 expect(type).toBe("text");305 }306 });307308 test("23 - clipboard clear option in settings", async () => {309 const { popup } = await createVault(ctx);310 await popup.locator("[aria-label*='settings' i]").click();311 await popup.waitForTimeout(500);312 await expect(popup.locator("text=clipboard clear")).toBeVisible();313 });314315 test("24 - clipboard clear time options: 30s, 60s, 90s, never", async () => {316 const { popup } = await createVault(ctx);317 await popup.locator("[aria-label*='settings' i]").click();318 await popup.waitForTimeout(500);319 await popup.locator("text=clipboard clear").click();320 await popup.waitForTimeout(300);321 const options = popup.locator("[role='option' i], [role='menuitem' i]");322 const count = await options.count();323 expect(count).toBeGreaterThanOrEqual(3);324 });325326 test("25 - lock on close option in settings", async () => {327 const { popup } = await createVault(ctx);328 await popup.locator("[aria-label*='settings' i]").click();329 await popup.waitForTimeout(500);330 await expect(popup.locator("text=lock on close")).toBeVisible();331 });332333 test("26 - lock on close toggle works", async () => {334 const { popup } = await createVault(ctx);335 await popup.locator("[aria-label*='settings' i]").click();336 await popup.waitForTimeout(500);337 const toggle = popup.locator("text=lock on close").locator("..").locator("[role='switch' i], [type='checkbox']").first();338 if (await toggle.isVisible({ timeout: 2000 }).catch(() => false)) {339 await toggle.click();340 await popup.waitForTimeout(300);341 }342 });343344 test("27 - breach monitoring toggle in settings", async () => {345 const { popup } = await createVault(ctx);346 await popup.locator("[aria-label*='settings' i]").click();347 await popup.waitForTimeout(500);348 await expect(popup.locator("text=breach monitoring")).toBeVisible();349 });350351 test("28 - vault name displays in header", async () => {352 const { popup } = await createVault(ctx);353 const header = popup.locator("[class*='header' i], [class*='title' i]").first();354 const hasHeader = await header.isVisible({ timeout: 2000 }).catch(() => false);355 expect(hasHeader).toBeTruthy();356 });357358 test("29 - vault entry count shows in settings", async () => {359 const { popup } = await createVault(ctx);360 await popup.locator("[aria-label*='settings' i]").click();361 await popup.waitForTimeout(500);362 await expect(popup.locator("text=entries").first()).toBeVisible();363 });364365 test("30 - version number shows in settings", async () => {366 const { popup } = await createVault(ctx);367 await popup.locator("[aria-label*='settings' i]").click();368 await popup.waitForTimeout(500);369 const version = popup.locator("text=1.0.");370 const hasVersion = await version.isVisible({ timeout: 2000 }).catch(() => false);371 expect(hasVersion).toBeTruthy();372 });373374 test("31 - syncing indicator shows for pro users", async () => {375 const { popup } = await createVault(ctx);376 await popup.locator("[aria-label*='settings' i]").click();377 await popup.waitForTimeout(500);378 const syncRow = popup.locator("text=sync");379 await expect(syncRow).toBeVisible();380 });381382 test("32 - pro badge shows next to sync option", async () => {383 const { popup } = await createVault(ctx);384 await popup.locator("[aria-label*='settings' i]").click();385 await popup.waitForTimeout(500);386 const proBadge = popup.locator(".peach-pro-badge, [class*='pro' i]");387 const hasBadge = await proBadge.isVisible({ timeout: 2000 }).catch(() => false);388 expect(hasBadge).toBeTruthy();389 });390391 test("33 - lock screen has peach branding", async () => {392 const { popup } = await loadPopup(ctx);393 const brandElements = popup.locator(".peach-wordmark, .wordmark, text=peach, [class*='brand' i]");394 const hasBrand = await brandElements.first().isVisible({ timeout: 3000 }).catch(() => false);395 expect(hasBrand).toBeTruthy();396 });397398 test("34 - unlock screen shows tagline", async () => {399 const { popup } = await loadPopup(ctx);400 const tagline = popup.locator("text=secrets, text=your secrets, [class*='tagline' i]");401 const hasTagline = await tagline.first().isVisible({ timeout: 3000 }).catch(() => false);402 expect(hasTagline).toBeTruthy();403 });404405 test("35 - password strength indicator during vault creation", async () => {406 const { popup } = await loadPopup(ctx);407 const pw = popup.locator("#master-password");408 if (await pw.isVisible()) {409 await pw.fill("weak");410 await popup.waitForTimeout(300);411 const strength = popup.locator("[class*='strength' i], text=weak, text=strong");412 const hasStrength = await strength.first().isVisible({ timeout: 2000 }).catch(() => false);413 expect(hasStrength).toBeTruthy();414 }415 });416417 test("36 - password strength updates as user types", async () => {418 const { popup } = await loadPopup(ctx);419 const pw = popup.locator("#master-password");420 const confirm = popup.locator("#confirm-password");421 if (await pw.isVisible()) {422 await pw.fill("w");423 await popup.waitForTimeout(200);424 await pw.fill("we");425 await popup.waitForTimeout(200);426 await pw.fill("weak");427 await popup.waitForTimeout(200);428 await pw.fill("WeakPassword123!");429 await popup.waitForTimeout(200);430 }431 });432433 test("37 - passwords must match on create", async () => {434 const { popup } = await loadPopup(ctx);435 const pw = popup.locator("#master-password");436 const confirm = popup.locator("#confirm-password");437 if (await pw.isVisible()) {438 await pw.fill("password123");439 await confirm.fill("differentpassword");440 await popup.getByRole("button", { name: /create vault/i }).click();441 await popup.waitForTimeout(500);442 const error = popup.locator("text=mismatch, text=do not match, text=not match");443 await expect(error.first()).toBeVisible({ timeout: 3000 });444 }445 });446447 test("38 - passwords must be at least 8 chars", async () => {448 const { popup } = await loadPopup(ctx);449 const pw = popup.locator("#master-password");450 const confirm = popup.locator("#confirm-password");451 if (await pw.isVisible()) {452 await pw.fill("1234567");453 await confirm.fill("1234567");454 await popup.getByRole("button", { name: /create vault/i }).click();455 await popup.waitForTimeout(500);456 const error = popup.locator("text=8 characters, text=minimum, text=at least");457 await expect(error.first()).toBeVisible({ timeout: 3000 });458 }459 });460461 test("39 - vault creation shows loading state", async () => {462 const { popup } = await loadPopup(ctx);463 const pw = popup.locator("#master-password");464 const confirm = popup.locator("#confirm-password");465 if (await pw.isVisible()) {466 await pw.fill("testpassword123");467 await confirm.fill("testpassword123");468 await popup.getByRole("button", { name: /create vault/i }).click();469 await popup.waitForTimeout(300);470 const loading = popup.locator("[class*='loading' i], [class*='spinner' i], text=creating");471 const hasLoading = await loading.first().isVisible({ timeout: 3000 }).catch(() => false);472 expect(hasLoading).toBeTruthy();473 }474 });475476 test("40 - successful vault creation transitions to unlocked state", async () => {477 const { popup } = await loadPopup(ctx);478 const pw = popup.locator("#master-password");479 const confirm = popup.locator("#confirm-password");480 if (await pw.isVisible()) {481 await pw.fill("testpassword123");482 await confirm.fill("testpassword123");483 await popup.getByRole("button", { name: /create vault/i }).click();484 await popup.waitForTimeout(2500);485 const entries = popup.locator("[class*=entry]");486 await expect(entries.first()).toBeVisible({ timeout: 5000 });487 }488 });489490 test("41 - empty vault shows encouraging empty state message", async () => {491 const { popup } = await createVault(ctx);492 const emptyCopy = popup.locator("text=empty, text=no entries, text=get started, text=add your first");493 const hasMessage = await emptyCopy.first().isVisible({ timeout: 3000 }).catch(() => false);494 expect(hasMessage).toBeTruthy();495 });496497 test("42 - adding first entry has prominent CTA", async () => {498 const { popup } = await createVault(ctx);499 const cta = popup.locator("text=add entry, text=new entry, button:has-text('new')");500 await expect(cta.first()).toBeVisible();501 });502503 test("43 - header shows vault status (locked/unlocked)", async () => {504 const { popup } = await createVault(ctx);505 const status = popup.locator("[class*='status' i], [class*='badge' i]");506 const hasStatus = await status.first().isVisible({ timeout: 2000 }).catch(() => false);507 expect(hasStatus).toBeTruthy();508 });509510 test("44 - biometric unlock option shows if available", async () => {511 const { popup } = await loadPopup(ctx);512 const biometric = popup.locator("text=biometric, text=fingerprint, text=face id, text=touch id");513 const hasBio = await biometric.first().isVisible({ timeout: 2000 }).catch(() => false);514 });515516 test("45 - keyboard shortcut hint on lock screen", async () => {517 const { popup } = await loadPopup(ctx);518 const shortcut = popup.locator("text=enter, text=shortcut, text=ctrl+enter");519 const hasShortcut = await shortcut.first().isVisible({ timeout: 2000 }).catch(() => false);520 });521522 test("46 - remember master password option", async () => {523 const { popup } = await createVault(ctx);524 await popup.locator("[aria-label*='settings' i]").click();525 await popup.waitForTimeout(500);526 const remember = popup.locator("text=remember master password, text=master password reminder");527 const hasRemember = await remember.first().isVisible({ timeout: 2000 }).catch(() => false);528 });529530 test("47 - vault export encrypts with password", async () => {531 const { popup } = await createVault(ctx);532 await popup.locator("[aria-label*='settings' i]").click();533 await popup.waitForTimeout(300);534 await popup.getByRole("button", { name: /export/i }).click();535 await popup.waitForTimeout(500);536 const exportPw = popup.locator('input[type="password"]').first();537 await expect(exportPw).toBeVisible();538 });539540 test("48 - vault import shows file picker", async () => {541 const { popup } = await createVault(ctx);542 await popup.locator("[aria-label*='settings' i]").click();543 await popup.waitForTimeout(300);544 await popup.getByRole("button", { name: /import/i }).click();545 await popup.waitForTimeout(500);546 const fileInput = popup.locator('input[type="file"]');547 await expect(fileInput).toBeAttached();548 });549550 test("49 - password generator accessible from main view", async () => {551 const { popup } = await createVault(ctx);552 const genBtn = popup.locator("[aria-label*='generate' i], text=generator, text=generate password");553 await expect(genBtn.first()).toBeVisible({ timeout: 2000 });554 });555556 test("50 - search vault with no entries shows no results", async () => {557 const { popup } = await createVault(ctx);558 await popup.locator('input[placeholder*="search" i]').click();559 await popup.locator('input[placeholder*="search" i]').fill("nonexistent");560 await popup.waitForTimeout(500);561 const noResults = popup.locator("text=no results, text=not found, text=0 results");562 const hasNoResults = await noResults.first().isVisible({ timeout: 2000 }).catch(() => false);563 expect(hasNoResults).toBeTruthy();564 });565});566567test.describe("Vault Lifecycle Part 2", () => {568 let ctx: BrowserContext;569 test.afterEach(async () => { if (ctx) await ctx.close(); });570 test.beforeEach(async () => { ctx = await makeContext(); });571572 test("51 - changing auto-lock time persists", async () => {573 const { popup } = await createVault(ctx);574 await popup.locator("[aria-label*='settings' i]").click();575 await popup.waitForTimeout(300);576 await popup.locator("text=auto-lock").click();577 await popup.waitForTimeout(300);578 const option = popup.locator("[role='option' i], [role='menuitem' i]").nth(1);579 await option.click();580 await popup.waitForTimeout(300);581 await popup.keyboard.press("Escape");582 await popup.waitForTimeout(200);583 await popup.locator("[aria-label*='settings' i]").click();584 await popup.waitForTimeout(300);585 await popup.locator("text=auto-lock").click();586 await popup.waitForTimeout(300);587 });588589 test("52 - changing clipboard clear time persists", async () => {590 const { popup } = await createVault(ctx);591 await popup.locator("[aria-label*='settings' i]").click();592 await popup.waitForTimeout(300);593 await popup.locator("text=clipboard clear").click();594 await popup.waitForTimeout(300);595 const option = popup.locator("[role='option' i], [role='menuitem' i]").nth(1);596 await option.click();597 await popup.waitForTimeout(300);598 await popup.keyboard.press("Escape");599 });600601 test("53 - vault data survives browser restart", async () => {602 const { popup } = await createVault(ctx);603 await popup.locator('input[placeholder*="search" i]').click();604 await popup.getByRole("button", { name: /new entry/i }).click();605 await popup.locator(".peach-new-name-input").fill("RestartTest");606 await popup.locator('input[placeholder*="domain" i]').fill("https://restart.com");607 await popup.locator('input[placeholder*="username" i]').fill("[email protected]");608 await popup.locator(".peach-new-field-input[placeholder*='password' i]").fill("restartpass");609 await popup.getByRole("button", { name: /create/i }).click();610 await popup.waitForTimeout(1500);611 });612613 test("54 - clearing vault shows confirmation", async () => {614 const { popup } = await createVault(ctx);615 await popup.locator("[aria-label*='settings' i]").click();616 await popup.waitForTimeout(300);617 const dangerZone = popup.locator("text=clear vault, text=delete all, text=destroy vault");618 const hasDanger = await dangerZone.first().isVisible({ timeout: 2000 }).catch(() => false);619 });620621 test("55 - clear vault requires password confirmation", async () => {622 const { popup } = await createVault(ctx);623 await popup.locator("[aria-label*='settings' i]").click();624 await popup.waitForTimeout(300);625 });626});