2.8 KiB
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.addInitScriptso 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.clipboardAPI surface forwriteTextandreadText - Idempotent — calling twice on the same page just re-installs the same script
Usage: Basic Example
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
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<void>
page: Page— Playwright page (must be called before any navigation for the init script to take effect)- Returns
Promise<void>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.