vault-lifecycle.spec.ts

6.1 KB · raw

1import { test, expect, type BrowserContext } from "@playwright/test";2import {3  UNLOCKED,4  VAULT_PASSWORD,5  addEntry,6  loadPopup,7  lockVault,8  makeContext,9  reachCreateScreen,10  setLockOnClose,11  setupVault,12  unlockVault,13} from "./helpers/vault";1415// Every test creates a real vault (onboarding + Argon2id KDF + recovery16// confirmation ≈ 12s) and several unlock again, which runs Argon2 a second17// time. The global 30s cap is too tight for those.18test.describe.configure({ timeout: 90_000 });1920// This file used to hold 55 tests written against a UI that no longer exists21// (an 8-character password minimum, a "1.0." version string, a pro badge, a22// settings sheet reached by aria-label='settings'). None of them ever ran23// against a real vault, because the setup helper looked for #master-password24// while the onboarding carousel was still on screen and silently skipped25// vault creation. What is left is the part that is both real and not already26// covered by lock-screen.spec.ts: the create-vault gate, and persistence.272829test.describe("Vault creation gate", () => {30  let ctx: BrowserContext;31  test.afterEach(async () => { if (ctx) await ctx.close(); });32  test.beforeEach(async () => { ctx = await makeContext("peach-lifecycle"); });3334  test("VL01 - fresh install opens the intro carousel", async () => {35    const { popup } = await loadPopup(ctx);36    await expect(popup.locator(".peach-onboarding-card")).toBeVisible({ timeout: 5000 });37    await expect(popup.locator("#master-password")).toHaveCount(0);38  });3940  test("VL02 - the carousel leads to the create-vault form", async () => {41    const { popup } = await loadPopup(ctx);42    await reachCreateScreen(popup);43    await expect(popup.locator("#confirm-password")).toBeVisible();44    await expect(popup.locator(".peach-newvault-submit")).toBeDisabled();45  });4647  test("VL03 - a password under 12 characters blocks creation", async () => {48    const { popup } = await loadPopup(ctx);49    await reachCreateScreen(popup);50    await popup.locator("#master-password").fill("shortpw12");51    await popup.locator("#confirm-password").fill("shortpw12");52    const submit = popup.locator(".peach-newvault-submit");53    await expect(submit).toBeDisabled();54    await expect(submit).toHaveText(/more characters/i);55  });5657  test("VL04 - a long but weak password blocks creation", async () => {58    const { popup } = await loadPopup(ctx);59    await reachCreateScreen(popup);60    await popup.locator("#master-password").fill("password123456");61    await popup.locator("#confirm-password").fill("password123456");62    const submit = popup.locator(".peach-newvault-submit");63    await expect(submit).toBeDisabled();64    await expect(submit).toHaveText(/too weak/i);65  });6667  test("VL05 - mismatched confirmation blocks creation", async () => {68    const { popup } = await loadPopup(ctx);69    await reachCreateScreen(popup);70    await popup.locator("#master-password").fill(VAULT_PASSWORD);71    await popup.locator("#confirm-password").fill("correct horse battery stapl");72    const submit = popup.locator(".peach-newvault-submit");73    await expect(submit).toBeDisabled();74    await expect(submit).toHaveText(/don't match/i);75  });7677  test("VL06 - the strength meter reacts to the password", async () => {78    const { popup } = await loadPopup(ctx);79    await reachCreateScreen(popup);80    const label = popup.locator(".peach-strength-label");81    await popup.locator("#master-password").fill("aaaa");82    await expect(label).toHaveText("weak");83    await popup.locator("#master-password").fill(VAULT_PASSWORD);84    await expect(label).toHaveText(/good|strong/);85  });8687  test("VL07 - a passing password creates the vault and unlocks it", async () => {88    const { popup } = await setupVault(ctx);89    await expect(popup.locator(UNLOCKED)).toBeVisible();90  });91});9293test.describe("Vault persistence", () => {94  let ctx: BrowserContext;95  test.afterEach(async () => { if (ctx) await ctx.close(); });96  test.beforeEach(async () => { ctx = await makeContext("peach-lifecycle"); });9798  test("VL08 - a new vault shows the empty state", async () => {99    const { popup } = await setupVault(ctx);100    await expect(popup.locator(".peach-empty-copy")).toBeVisible({ timeout: 5000 });101  });102103  test("VL09 - entries survive lock and unlock", async () => {104    const { popup } = await setupVault(ctx);105    await addEntry(popup, { title: "LockRoundTrip", url: "https://roundtrip.test", username: "[email protected]", password: "entry-secret" });106    await lockVault(popup);107    await unlockVault(popup);108    await expect(popup.locator(".peach-entry-name-text").first()).toHaveText("LockRoundTrip", { timeout: 10000 });109  });110111  test("VL10 - reopening the popup requires the password again", async () => {112    const { popup } = await setupVault(ctx);113    await addEntry(popup, { title: "ReopenTest", url: "https://reopen.test", username: "[email protected]", password: "entry-secret" });114    await setLockOnClose(popup, true);115    await lockVault(popup);116    await popup.close();117118    const { popup: reopened } = await loadPopup(ctx);119    await expect(reopened.locator("#unlock-password")).toBeVisible({ timeout: 15000 });120    await unlockVault(reopened);121    await expect(reopened.locator(".peach-entry-name-text").first()).toHaveText("ReopenTest", { timeout: 10000 });122  });123124  test("VL11 - a wrong password does not unlock a reopened popup", async () => {125    const { popup } = await setupVault(ctx);126    await setLockOnClose(popup, true);127    await lockVault(popup);128    await popup.close();129130    const { popup: reopened } = await loadPopup(ctx);131    await expect(reopened.locator("#unlock-password")).toBeVisible({ timeout: 15000 });132    await reopened.locator("#unlock-password").fill("not the right password");133    await reopened.getByRole("button", { name: /unlock/i }).click();134    await expect(reopened.locator(".peach-hint-error")).toBeVisible({ timeout: 15000 });135    await expect(reopened.locator(UNLOCKED)).toHaveCount(0);136  });137138  test("VL12 - default popup close leaves the vault unlocked", async () => {139    const { popup } = await setupVault(ctx);140    await popup.close();141142    const { popup: reopened } = await loadPopup(ctx);143    await expect(reopened.locator(UNLOCKED)).toBeVisible({ timeout: 15000 });144  });145});