12345678910111213141516171819202122232425import { test, expect } from "@playwright/test";26import type { BrowserContext, Page } from "@playwright/test";27import { fileURLToPath } from "url";28import { dirname, join } from "path";29import os from "os";30import fs from "fs/promises";3132const __filename = fileURLToPath(import.meta.url);33const __dirname = dirname(__filename);34const EXTENSION_PATH = join(__dirname, "..", "dist");35const FIXTURE = join(__dirname, "fixtures", "login-form.html");36const ICON = 'button[aria-label="Peach autofill"]';3738async function makeContext(): Promise<BrowserContext> {39 const { chromium } = await import("playwright");40 const userDataDir = join(os.tmpdir(), `peach-e2e-${Date.now()}`);41 await fs.mkdir(userDataDir, { recursive: true });42 return chromium.launchPersistentContext(userDataDir, {43 headless: false,44 args: [`--load-extension=${EXTENSION_PATH}`, `--disable-extensions-except=${EXTENSION_PATH}`],45 });46}4748495051async function setupVaultWithEntry(_ctx: BrowserContext, _origin: string): Promise<void> {52 53 54}5556575859async function openFixture(ctx: BrowserContext): Promise<{ page: Page; origin: string }> {60 const page = await ctx.newPage();61 await page.goto(`file://${FIXTURE}`); 62 return { page, origin: new URL(page.url()).origin };63}6465test.describe("Real-page autofill e2e (T-E01–T-E10)", () => {66 let ctx: BrowserContext;6768 test.beforeAll(async () => {69 ctx = await makeContext();70 });71 test.afterAll(async () => {72 await ctx?.close();73 });7475 76 test("T-E01: field icon appears in the password field", async () => {77 const { page } = await openFixture(ctx);78 await expect(page.locator(ICON)).toBeVisible();79 await page.close();80 });8182 83 84 test("T-E02: clicking the icon opens the selector for domain-matched entries", async () => {85 const { page, origin } = await openFixture(ctx);86 await setupVaultWithEntry(ctx, origin);87 await page.locator(ICON).click();88 await expect(page.locator('div[style*="2147483647"]')).toBeVisible();89 await page.close();90 });9192 93 94 test("T-E03: selecting an entry fills username and password", async () => {95 const { page, origin } = await openFixture(ctx);96 await setupVaultWithEntry(ctx, origin);97 await page.locator(ICON).click();98 99 100 101 await page.keyboard.press("Enter");102 await expect(page.locator("#username")).toHaveValue("alice");103 await expect(page.locator("#password")).toHaveValue("s3cret");104 await page.close();105 });106107 108 test("T-E04: after fill the selector closes, the icon remains", async () => {109 const { page, origin } = await openFixture(ctx);110 await setupVaultWithEntry(ctx, origin);111 await page.locator(ICON).click();112 await page.keyboard.press("Enter");113 await expect(page.locator('div[style*="2147483647"]')).toHaveCount(0);114 await expect(page.locator(ICON)).toBeVisible();115 await page.close();116 });117118 119 120 test("T-E05: clicking the filled field within the grace window does not reopen", async () => {121 const { page, origin } = await openFixture(ctx);122 await setupVaultWithEntry(ctx, origin);123 await page.locator(ICON).click();124 await page.keyboard.press("Enter");125 await page.locator("#password").click();126 await expect(page.locator('div[style*="2147483647"]')).toHaveCount(0);127 await page.close();128 });129130 131 test("T-E06: after the grace window the selector opens again", async () => {132 const { page, origin } = await openFixture(ctx);133 await setupVaultWithEntry(ctx, origin);134 await page.locator(ICON).click();135 await page.keyboard.press("Enter");136 await page.waitForTimeout(3200); 137 await page.locator("#password").click();138 await expect(page.locator('div[style*="2147483647"]')).toBeVisible();139 await page.close();140 });141142 143 144 test("T-E07: form submit after fill does not re-prompt to save unchanged creds", async () => {145 const { page, origin } = await openFixture(ctx);146 await setupVaultWithEntry(ctx, origin);147 await page.locator(ICON).click();148 await page.keyboard.press("Enter");149 await page.evaluate(() => (document.getElementById("login-form") as HTMLFormElement).requestSubmit());150 151 await page.waitForTimeout(500);152 await expect(page.locator('div[style*="2147483647"]')).toHaveCount(0);153 await page.close();154 });155156 157 158 test("T-E08: email-first form gets an email icon and opens", async () => {159 const { page } = await openFixture(ctx);160 await page.evaluate(() => ((document.getElementById("email-first-form") as HTMLElement).style.display = "block"));161 await expect(page.locator(ICON)).toHaveCount(1); 162 await page.close();163 });164165 166 test("T-E09: signup form detection shows the identity surface", async () => {167 const { page } = await openFixture(ctx);168 await page.evaluate(() => ((document.getElementById("signup-form") as HTMLElement).style.display = "block"));169 await page.locator("#new-password").click();170 await expect(page.locator('div[style*="2147483647"]')).toBeVisible();171 await page.close();172 });173174 175 176 test("T-E10: cross-origin iframe present → no crash, main form still fills", async () => {177 const { page, origin } = await openFixture(ctx);178 await setupVaultWithEntry(ctx, origin);179 await page.evaluate(() => {180 const f = document.createElement("iframe");181 f.src = "https://example.com/";182 document.body.appendChild(f);183 });184 await page.locator(ICON).first().click();185 await page.keyboard.press("Enter");186 await expect(page.locator("#password")).toHaveValue("s3cret");187 await page.close();188 });189});