# mock-clipboard ## Purpose `mockClipboard` installs a deterministic, in-memory clipboard implementation on the navigator object for a Playwright page. The default clipboard shipped by Chromium headless requires explicit user-gesture permissions and does not work at all in some CI environments; this helper replaces it with a programmatic pair of `writeText` / `readText` that behaves identically in every environment. ## Features - Installs via `page.addInitScript` so the mock is active before any app code runs - In-memory state is per-page — different pages don't share clipboard state - Matches the real `navigator.clipboard` API surface for `writeText` and `readText` - Idempotent — calling twice on the same page just re-installs the same script ## Usage: Basic Example ```ts import { mockClipboard } from "@suaveplan/testing-e2e/helpers"; await mockClipboard(page); await page.goto("/settings"); await page.click("text=Copy API key"); const copied = await page.evaluate(() => navigator.clipboard.readText()); expect(copied).toMatch(/^sk_/); ``` ## Usage: Advanced Example ```ts import { mockClipboard } from "@suaveplan/testing-e2e/helpers"; import { expect } from "@playwright/test"; test("copy + paste round trip", async ({ page }) => { await mockClipboard(page); await page.goto("/notes/123"); await page.getByRole("button", { name: /copy link/i }).click(); const copied = await page.evaluate(() => navigator.clipboard.readText()); expect(copied).toBe("https://notes.example/123"); // Paste somewhere: await page.goto("/notes/new"); await page.evaluate(async () => { const text = await navigator.clipboard.readText(); (document.querySelector("textarea") as HTMLTextAreaElement).value = text; }); await expect(page.locator("textarea")).toHaveValue("https://notes.example/123"); }); ``` ## API Reference ### `mockClipboard(page): Promise` - `page: Page` — Playwright page (must be called before any navigation for the init script to take effect) - Returns `Promise` that resolves after the init script is registered ## Implementation Notes The init script defines `navigator.clipboard` using `Object.defineProperty` with `writable: true` so a test that wants to swap in a further spy can still do so. Because the mock is installed via `addInitScript`, it's reinstalled automatically on every navigation within the same page — tests don't need to reinstall after `page.goto`. ### Behaviour `mockClipboard(page)` installs an `addInitScript` that replaces `navigator.clipboard.readText` / `writeText` with in-memory stubs whose state survives page navigations inside the same context. A companion `getClipboardValue(page)` reads the current mock value synchronously from the page's JS context so assertions do not need to await a clipboard-permissions prompt.