</>

Technology

Playwright

Difficulty

Intermediate

Interview Question

How would you handle a Playwright test that needs to verify clipboard copy functionality?

Answer

Scenario: Testing Clipboard Copy Functionality

Many modern apps have "Copy to Clipboard" buttons. Testing clipboard access requires browser permissions.

Method 1 — Grant Clipboard Permission and Read

TypeScript
import { test, expect } from '@playwright/test';

test('copy to clipboard button copies the correct text', async ({ page, context }) => {
  // Grant clipboard-read permission
  await context.grantPermissions(['clipboard-read', 'clipboard-write']);

  await page.goto('/share');

  // Get the text that should be copied
  const expectedUrl = await page.locator('.share-url').textContent();

  // Click the copy button
  await page.getByRole('button', { name: 'Copy Link' }).click();

  // Verify visual feedback (most apps show this)
  await expect(page.getByRole('button', { name: 'Copied!' })).toBeVisible();

  // Read from clipboard to verify what was actually copied
  const clipboardText = await page.evaluate(() => navigator.clipboard.readText());
  expect(clipboardText).toBe(expectedUrl?.trim());
});

Method 2 — Mock the Clipboard API

TypeScript
test('copy button uses clipboard API', async ({ page }) => {
  // Mock clipboard.writeText to capture what was written
  await page.addInitScript(() => {
    let copiedText = '';
    Object.defineProperty(navigator, 'clipboard', {
      value: {
        writeText: (text: string) => {
          copiedText = text;
          (window as any).__copiedText = text;
          return Promise.resolve();
        },
        readText: () => Promise.resolve(copiedText),
      },
      writable: true,
    });
  });

  await page.goto('/api-docs');
  await page.getByRole('button', { name: 'Copy API Key' }).click();

  // Read what the mock captured
  const copiedText = await page.evaluate(() => (window as any).__copiedText);
  expect(copiedText).toMatch(/^[A-Za-z0-9]{32}$/); // API key format
});

Method 3 — Intercept the Paste (Write then Read)

TypeScript
test('copy and paste workflow', async ({ page, context }) => {
  await context.grantPermissions(['clipboard-read', 'clipboard-write']);

  await page.goto('/code-editor');

  // Select code snippet
  const codeBlock = page.locator('.code-snippet').first();
  await codeBlock.getByRole('button', { name: 'Copy' }).click();

  // Navigate to another input and paste
  const pasteTarget = page.locator('#code-input');
  await pasteTarget.focus();
  await page.keyboard.press('Control+V'); // Or Meta+V on Mac

  // Verify pasted content
  const pastedValue = await pasteTarget.inputValue();
  expect(pastedValue).toContain('npm install playwright');
});

Method 4 — Test Keyboard Shortcut Copy

TypeScript
test('Ctrl+C copies selected text', async ({ page, context }) => {
  await context.grantPermissions(['clipboard-read', 'clipboard-write']);
  await page.goto('/docs');

  const paragraph = page.locator('p').first();

  // Select all text in the paragraph
  await paragraph.click({ clickCount: 3 }); // Triple-click to select all

  // Copy with keyboard
  await page.keyboard.press('Control+C');

  // Read clipboard
  const copied = await page.evaluate(() => navigator.clipboard.readText());
  const expected = await paragraph.textContent();

  expect(copied.trim()).toBe(expected?.trim());
});

Common Clipboard Test Patterns

TypeScript
// Verify the "Copied!" tooltip appears briefly
test('copy feedback tooltip', async ({ page, context }) => {
  await context.grantPermissions(['clipboard-read', 'clipboard-write']);
  await page.goto('/profile');

  await page.getByRole('button', { name: 'Copy ID' }).click();

  // Verify tooltip appears
  await expect(page.getByRole('tooltip')).toHaveText('Copied!');

  // Verify tooltip disappears after 2 seconds
  await expect(page.getByRole('tooltip')).not.toBeVisible({ timeout: 3000 });
});

Note: Playwright requires explicit grantPermissions(['clipboard-read', 'clipboard-write']) since clipboard access is a browser security permission. Without it, navigator.clipboard.readText() will throw.

Follow AutomateQA

Related Topics