entry-crud.spec.ts

8.4 KB · raw

1import { test, expect } from "@playwright/test";2import type { BrowserContext } from "@playwright/test";3import { fileURLToPath } from "url";4import { dirname, join } from "path";5import os from "os";6import fs from "fs/promises";78const __filename = fileURLToPath(import.meta.url);9const __dirname = dirname(__filename);10const EXTENSION_PATH = join(__dirname, "..", "dist");1112async function makeContext() {13  const { chromium } = await import("playwright");14  const userDataDir = join(os.tmpdir(), `peach-test-${Date.now()}`);15  await fs.mkdir(userDataDir, { recursive: true });16  return chromium.launchPersistentContext(userDataDir, {17    headless: false,18    args: [`--load-extension=${EXTENSION_PATH}`, `--disable-extensions-except=${EXTENSION_PATH}`],19  });20}2122async function createVaultAndAddEntry(ctx: BrowserContext): Promise<{ popup: import("@playwright/test").Page, extId: string }> {23  const maxWait = 15000;24  const start = Date.now();25  let sw = ctx.serviceWorkers()[0];26  while (!sw && Date.now() - start < maxWait) {27    await new Promise(r => setTimeout(r, 500));28    sw = ctx.serviceWorkers()[0];29  }30  if (!sw) throw new Error(`No SW after ${maxWait}ms`);31  const extId = new URL(sw.url()).host;32  const popup = await ctx.newPage();33  await popup.goto(`chrome-extension://${extId}/src/popup/index.html`);34  await popup.waitForTimeout(2000);35  const pw = popup.locator("#master-password");36  if (await pw.isVisible()) {37    await pw.fill("testpassword123");38    await popup.locator("#confirm-password").fill("testpassword123");39    await popup.getByRole("button", { name: /create vault/i }).click();40    await popup.waitForTimeout(2000);41  }42  return { popup, extId };43}4445test.describe("Entry CRUD", () => {46  let ctx: BrowserContext;47  test.afterEach(async () => { await ctx.close(); });48  test.beforeEach(async () => { ctx = await makeContext(); });4950  test("07 - add entry with all fields", async () => {51    const { popup } = await createVaultAndAddEntry(ctx);52    await popup.locator('input[placeholder="search vault..."]').click();53    await popup.getByRole("button", { name: /new entry/i }).click();54    await popup.locator(".peach-new-name-input").fill("GitHub");55    await popup.locator('input[placeholder="domain.com"]').fill("https://github.com");56    await popup.locator('input[placeholder="username or email"]').fill("[email protected]");57    await popup.locator(".peach-new-field-input[placeholder='password']").fill("secret123!");58    await popup.getByRole("button", { name: /create/i }).click();59    await popup.waitForTimeout(1500);60    await expect(popup.locator("text=GitHub").first()).toBeVisible({ timeout: 5000 });61  });6263  test("08 - add entry with TOTP", async () => {64    const { popup } = await createVaultAndAddEntry(ctx);65    await popup.locator('input[placeholder="search vault..."]').click();66    await popup.getByRole("button", { name: /new entry/i }).click();67    await popup.locator(".peach-new-name-input").fill("GitHub2FA");68    await popup.locator('input[placeholder="domain.com"]').fill("https://github.com");69    await popup.locator('input[placeholder="username or email"]').fill("[email protected]");70    await popup.locator(".peach-new-field-input[placeholder='password']").fill("secret123!");71    await popup.locator('input[placeholder*="totp" i], input[placeholder*="base32" i]').fill("JBSWY3DPEHPK3PXP");72    await popup.getByRole("button", { name: /create/i }).click();73    await popup.waitForTimeout(1500);74    await expect(popup.locator("text=GitHub2FA").first()).toBeVisible({ timeout: 5000 });75    await expect(popup.locator(".peach-totp-code")).toBeVisible({ timeout: 3000 });76  });7778  test("09 - edit entry changes persist", async () => {79    const { popup } = await createVaultAndAddEntry(ctx);80    await popup.locator('input[placeholder="search vault..."]').click();81    await popup.getByRole("button", { name: /new entry/i }).click();82    await popup.locator(".peach-new-name-input").fill("EditTest");83    await popup.locator('input[placeholder="domain.com"]').fill("https://edittest.com");84    await popup.locator('input[placeholder="username or email"]').fill("[email protected]");85    await popup.locator(".peach-new-field-input[placeholder='password']").fill("oldpass");86    await popup.getByRole("button", { name: /create/i }).click();87    await popup.waitForTimeout(1500);88    await popup.locator("text=EditTest").first().click();89    await popup.waitForTimeout(500);90    const editBtn = popup.locator("button[aria-label='Edit'], .peach-entry-name-button").first();91    await editBtn.click();92    await popup.waitForTimeout(500);93    await popup.locator(".peach-new-field-input[placeholder='password']").fill("newpass");94    await popup.getByRole("button", { name: /save/i }).click();95    await popup.waitForTimeout(1500);96    await popup.locator('input[placeholder="search vault..."]').click();97    await popup.waitForTimeout(500);98    await popup.locator("text=EditTest").first().click();99    await popup.waitForTimeout(500);100    await expect(popup.locator("text=newpass").first()).toBeVisible({ timeout: 3000 });101  });102103  test("10 - delete entry removes from vault", async () => {104    const { popup } = await createVaultAndAddEntry(ctx);105    await popup.locator('input[placeholder="search vault..."]').click();106    await popup.getByRole("button", { name: /new entry/i }).click();107    await popup.locator(".peach-new-name-input").fill("DeleteMe");108    await popup.locator('input[placeholder="domain.com"]').fill("https://deleteme.com");109    await popup.locator('input[placeholder="username or email"]').fill("[email protected]");110    await popup.locator(".peach-new-field-input[placeholder='password']").fill("deletepass");111    await popup.getByRole("button", { name: /create/i }).click();112    await popup.waitForTimeout(1500);113    await popup.locator("text=DeleteMe").first().click();114    await popup.waitForTimeout(500);115    await popup.locator("text=delete entry").click();116    await popup.waitForTimeout(1000);117    await expect(popup.locator("text=DeleteMe").first()).not.toBeVisible();118  });119120  test("11 - search filters entries by title", async () => {121    const { popup } = await createVaultAndAddEntry(ctx);122    for (const name of ["Apple", "Amazon", "Google"]) {123      await popup.locator('input[placeholder="search vault..."]').click();124      await popup.getByRole("button", { name: /new entry/i }).click();125      await popup.locator(".peach-new-name-input").fill(name);126      await popup.locator('input[placeholder="domain.com"]').fill(`https://${name.toLowerCase()}.com`);127      await popup.locator('input[placeholder="username or email"]').fill(`u@${name.toLowerCase()}.com`);128      await popup.locator(".peach-new-field-input[placeholder='password']").fill("pass123");129      await popup.getByRole("button", { name: /create/i }).click();130      await popup.waitForTimeout(800);131    }132    await popup.locator('input[placeholder="search vault..."]').click();133    await popup.waitForTimeout(300);134    const searchInput = popup.locator(".peach-search-input, .peach-search-input-list").first();135    await searchInput.fill("Apple");136    await popup.waitForTimeout(500);137    await expect(popup.locator("text=Apple")).toBeVisible();138    await expect(popup.locator("text=Amazon")).not.toBeVisible();139  });140141  test("12 - prev/next navigation cycles through entries", async () => {142    const { popup } = await createVaultAndAddEntry(ctx);143    for (const name of ["FirstEntry", "SecondEntry"]) {144      await popup.locator('input[placeholder="search vault..."]').click();145      await popup.getByRole("button", { name: /new entry/i }).click();146      await popup.locator(".peach-new-name-input").fill(name);147      await popup.locator('input[placeholder="domain.com"]').fill(`https://${name.toLowerCase()}.com`);148      await popup.locator('input[placeholder="username or email"]').fill("[email protected]");149      await popup.locator(".peach-new-field-input[placeholder='password']").fill("pass");150      await popup.getByRole("button", { name: /create/i }).click();151      await popup.waitForTimeout(800);152    }153    const nextBtn = popup.getByRole("button", { name: /next/i });154    await nextBtn.click();155    await popup.waitForTimeout(500);156    const content = popup.locator(".peach-content-frame, .peach-stage").first();157    await expect(content).toBeVisible();158  });159160  test("13 - empty vault shows empty state", async () => {161    const { popup } = await createVaultAndAddEntry(ctx);162    await expect(popup.locator(".peach-empty-copy, .peach-entry-name, text=empty vault").first()).toBeVisible({ timeout: 3000 });163  });164});