import { test, expect } from "@playwright/test"; import type { BrowserContext } from "@playwright/test"; import { fileURLToPath } from "url"; import { dirname, join } from "path"; import os from "os"; import fs from "fs/promises"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const EXTENSION_PATH = join(__dirname, "..", "dist"); async function makeContext() { const { chromium } = await import("playwright"); const userDataDir = join(os.tmpdir(), `peach-test-${Date.now()}`); await fs.mkdir(userDataDir, { recursive: true }); return chromium.launchPersistentContext(userDataDir, { headless: false, args: [`--load-extension=${EXTENSION_PATH}`, `--disable-extensions-except=${EXTENSION_PATH}`], }); } async function setupVault(ctx: BrowserContext): Promise<{ popup: import("@playwright/test").Page, extId: string }> { const maxWait = 15000; const start = Date.now(); let sw = ctx.serviceWorkers()[0]; while (!sw && Date.now() - start < maxWait) { await new Promise(r => setTimeout(r, 500)); sw = ctx.serviceWorkers()[0]; } if (!sw) throw new Error(`No SW after ${maxWait}ms`); const extId = new URL(sw.url()).host; const popup = await ctx.newPage(); await popup.goto(`chrome-extension://${extId}/src/popup/index.html`); await popup.waitForTimeout(2000); const pw = popup.locator("#master-password"); if (await pw.isVisible()) { await pw.fill("password123"); await popup.locator("#confirm-password").fill("password123"); await popup.getByRole("button", { name: /create vault/i }).click(); await popup.waitForTimeout(2000); } return { popup, extId }; } test.describe("Autofill Content Script", () => { let ctx: BrowserContext; test.afterEach(async () => { await ctx.close(); }); test.beforeEach(async () => { ctx = await makeContext(); }); test("31 - peach icon injected into page with password field", async () => { const { popup: _popup } = await setupVault(ctx); const testPage = await ctx.newPage(); await testPage.setContent(`
`); await testPage.waitForTimeout(2000); testPage.locator("button", { hasText: "🍑" }); await testPage.waitForTimeout(500); }); test("32 - autofill selector appears on icon click", async () => { const { popup } = await setupVault(ctx); await popup.locator('input[placeholder="search vault..."]').click(); await popup.getByRole("button", { name: /new entry/i }).click(); await popup.locator(".peach-new-name-input").fill("TestForm"); await popup.locator('input[placeholder="domain.com"]').fill("https://example.com"); await popup.locator('input[placeholder="username or email"]').fill("testuser"); await popup.locator('input[placeholder="password"]').fill("testpass"); await popup.getByRole("button", { name: /create/i }).click(); await popup.waitForTimeout(1500); const testPage = await ctx.newPage(); await testPage.setContent(` `); await testPage.waitForTimeout(2000); await testPage.waitForTimeout(500); }); test("33 - no fill without user interaction", async () => { const { popup } = await setupVault(ctx); await popup.locator('input[placeholder="search vault..."]').click(); await popup.getByRole("button", { name: /new entry/i }).click(); await popup.locator(".peach-new-name-input").fill("SilentFillTest"); await popup.locator('input[placeholder="domain.com"]').fill("https://silentfill.com"); await popup.locator('input[placeholder="username or email"]').fill("silent@user.com"); await popup.locator('input[placeholder="password"]').fill("silentpass"); await popup.getByRole("button", { name: /create/i }).click(); await popup.waitForTimeout(1500); const testPage = await ctx.newPage(); await testPage.setContent(``); await testPage.waitForTimeout(1000); const pwInput = testPage.locator("#p"); const value = await pwInput.inputValue(); expect(value).toBe(""); }); test("34 - hidden password fields are not filled", async () => { const testPage = await ctx.newPage(); await testPage.setContent(` `); await testPage.waitForTimeout(1000); const pwInput = testPage.locator("#p"); const value = await pwInput.inputValue(); expect(value).toBe(""); }); }); test.describe("Import Export", () => { let ctx: BrowserContext; test.afterEach(async () => { await ctx.close(); }); test.beforeEach(async () => { ctx = await makeContext(); }); test("35 - export produces downloadable JSON file", async () => { const { popup } = await setupVault(ctx); await popup.locator('input[placeholder="search vault..."]').click(); await popup.getByRole("button", { name: /new entry/i }).click(); await popup.locator(".peach-new-name-input").fill("ExportTest"); await popup.locator('input[placeholder="domain.com"]').fill("https://exporttest.com"); await popup.locator('input[placeholder="username or email"]').fill("export@user.com"); await popup.locator(".peach-new-field-input[placeholder='password']").fill("exportpass"); await popup.getByRole("button", { name: /create/i }).click(); await popup.waitForTimeout(1500); await popup.locator(".peach-icon-btn[aria-label='Settings']").click(); await popup.waitForTimeout(300); await popup.getByRole("button", { name: /export backup/i }).click(); await popup.waitForTimeout(500); const exportPasswordInput = popup.locator(".form-input[type='password']"); await exportPasswordInput.fill("exportpassword"); const downloadBtn = popup.getByRole("button", { name: /download backup/i }); await expect(downloadBtn).toBeVisible(); }); test("36 - CSV import requires column mapping", async () => { const { popup } = await setupVault(ctx); await popup.locator(".peach-icon-btn[aria-label='Settings']").click(); await popup.waitForTimeout(300); await popup.getByRole("button", { name: /import passwords/i }).click(); await popup.waitForTimeout(500); const fileInput = popup.locator('input[type="file"]'); await expect(fileInput).toBeAttached(); }); });