autofill.spec.ts

6.7 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 setupVault(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("password123");38    await popup.locator("#confirm-password").fill("password123");39    await popup.getByRole("button", { name: /create vault/i }).click();40    await popup.waitForTimeout(2000);41  }42  return { popup, extId };43}4445test.describe("Autofill Content Script", () => {46  let ctx: BrowserContext;47  test.afterEach(async () => { await ctx.close(); });48  test.beforeEach(async () => { ctx = await makeContext(); });4950  test("31 - peach icon injected into page with password field", async () => {51    const { popup: _popup } = await setupVault(ctx);52    const testPage = await ctx.newPage();53    await testPage.setContent(`54      <html><body>55        <form action="/login" method="post">56          <input type="text" id="username" name="username" autocomplete="username">57          <input type="password" id="password" name="password" autocomplete="current-password">58          <button type="submit">Login</button>59        </form>60      </body></html>61    `);62    await testPage.waitForTimeout(2000);63    testPage.locator("button", { hasText: "🍑" });64    await testPage.waitForTimeout(500);65  });6667  test("32 - autofill selector appears on icon click", async () => {68    const { popup } = await setupVault(ctx);69    await popup.locator('input[placeholder="search vault..."]').click();70    await popup.getByRole("button", { name: /new entry/i }).click();71    await popup.locator(".peach-new-name-input").fill("TestForm");72    await popup.locator('input[placeholder="domain.com"]').fill("https://example.com");73    await popup.locator('input[placeholder="username or email"]').fill("testuser");74    await popup.locator('input[placeholder="password"]').fill("testpass");75    await popup.getByRole("button", { name: /create/i }).click();76    await popup.waitForTimeout(1500);77    const testPage = await ctx.newPage();78    await testPage.setContent(`79      <html><body>80        <form>81          <input type="text" id="u" autocomplete="username">82          <input type="password" id="p" autocomplete="current-password">83        </form>84      </body></html>85    `);86    await testPage.waitForTimeout(2000);87    await testPage.waitForTimeout(500);88  });8990  test("33 - no fill without user interaction", async () => {91    const { popup } = await setupVault(ctx);92    await popup.locator('input[placeholder="search vault..."]').click();93    await popup.getByRole("button", { name: /new entry/i }).click();94    await popup.locator(".peach-new-name-input").fill("SilentFillTest");95    await popup.locator('input[placeholder="domain.com"]').fill("https://silentfill.com");96    await popup.locator('input[placeholder="username or email"]').fill("[email protected]");97    await popup.locator('input[placeholder="password"]').fill("silentpass");98    await popup.getByRole("button", { name: /create/i }).click();99    await popup.waitForTimeout(1500);100    const testPage = await ctx.newPage();101    await testPage.setContent(`<html><body><input type="password" id="p"></body></html>`);102    await testPage.waitForTimeout(1000);103    const pwInput = testPage.locator("#p");104    const value = await pwInput.inputValue();105    expect(value).toBe("");106  });107108  test("34 - hidden password fields are not filled", async () => {109    const testPage = await ctx.newPage();110    await testPage.setContent(`111      <html><body>112        <input type="password" id="p" style="display:none">113      </body></html>114    `);115    await testPage.waitForTimeout(1000);116    const pwInput = testPage.locator("#p");117    const value = await pwInput.inputValue();118    expect(value).toBe("");119  });120});121122test.describe("Import Export", () => {123  let ctx: BrowserContext;124  test.afterEach(async () => { await ctx.close(); });125  test.beforeEach(async () => { ctx = await makeContext(); });126127  test("35 - export produces downloadable JSON file", async () => {128    const { popup } = await setupVault(ctx);129    await popup.locator('input[placeholder="search vault..."]').click();130    await popup.getByRole("button", { name: /new entry/i }).click();131    await popup.locator(".peach-new-name-input").fill("ExportTest");132    await popup.locator('input[placeholder="domain.com"]').fill("https://exporttest.com");133    await popup.locator('input[placeholder="username or email"]').fill("[email protected]");134    await popup.locator(".peach-new-field-input[placeholder='password']").fill("exportpass");135    await popup.getByRole("button", { name: /create/i }).click();136    await popup.waitForTimeout(1500);137    await popup.locator(".peach-icon-btn[aria-label='Settings']").click();138    await popup.waitForTimeout(300);139    await popup.getByRole("button", { name: /export backup/i }).click();140    await popup.waitForTimeout(500);141    const exportPasswordInput = popup.locator(".form-input[type='password']");142    await exportPasswordInput.fill("exportpassword");143    const downloadBtn = popup.getByRole("button", { name: /download backup/i });144    await expect(downloadBtn).toBeVisible();145  });146147  test("36 - CSV import requires column mapping", async () => {148    const { popup } = await setupVault(ctx);149    await popup.locator(".peach-icon-btn[aria-label='Settings']").click();150    await popup.waitForTimeout(300);151    await popup.getByRole("button", { name: /import passwords/i }).click();152    await popup.waitForTimeout(500);153    const fileInput = popup.locator('input[type="file"]');154    await expect(fileInput).toBeAttached();155  });156});