playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Feature] a dedicated clipboard API

Open aslushnikov opened this issue 3 years ago • 7 comments
trafficstars

It might be nice to have a dedicated clipboard API scoped to page.

Some ideas:

await page.pasteText(text);
await page.keyboard.press('Ctrl + V'); 
await page.copySelection();
await context.clipboard.setContent(mimeType, content);

aslushnikov avatar Jul 21 '22 21:07 aslushnikov

Would be great.

nikolay-yavorovskiy avatar Aug 03 '22 17:08 nikolay-yavorovskiy

Would really help us out as well!

hxnir avatar Dec 18 '22 13:12 hxnir

+1

cscheffauer avatar Jan 16 '23 19:01 cscheffauer

My current workaround is this (C#)

public async Task<string> GetClipboardAsync()
{
    IPage page = await Context.Context.NewPageAsync();

    await Context.CurrentPage.SetContentAsync("<textarea id='target'></textarea>");

    ILocator target = page.Locator("xpath=//textarea[@id='target']");
    await target.FocusAsync();
    string controlKey = page.Context.Browser!.BrowserType.Name is "webkit" ? "Meta" : "Control";
    await target.PressAsync($"{controlKey}+v");

    string text = await target.InputValueAsync();
    await page.CloseAsync();
    return text;
}

public async Task SetClipboardAsync(string value)
{
    IPage page = await Context.Context.NewPageAsync();
    await page.SetContentAsync($"<textarea id='target'>{value}</textarea>");

    ILocator target = page.Locator("xpath=//textarea[@id='target']");
    await target .SelectTextAsync(); // Focus & Ctrl+a
    string controlKey = page.Context.Browser!.BrowserType.Name is "webkit" ? "Meta" : "Control";
    await target.PressAsync($"{controlKey}+c");

    await page.CloseAsync();
}

Note the control key won't work if you're using non-safari on macOS. Getting the operating system can be a bit of a hassle (you need javascript, navigator.userAgentData is experimental, parsing navigator.userAgent isn't trivial, etc). So this solution will need tweaking if (unlike me) you test that browser-OS permutation.

RenderMichael avatar Jan 18 '23 17:01 RenderMichael

Wrote the following to simulate copy and paste events for testing a component that handles them:

import type { JSHandle, Locator } from '@playwright/test';

/**
 * Copy the selection to the clipboard and return a `JSHandle` to the data.
 */
export async function copy(locator: Locator): Promise<JSHandle<DataTransfer>> {
  return locator.evaluateHandle(() => {
    const event = new ClipboardEvent('copy', { bubbles: true, cancelable: true, clipboardData: new DataTransfer() });
    window.getSelection()?.anchorNode?.dispatchEvent(event);
    return event.clipboardData;
  });
}

/**
 * Paste the given data at the selection.
 */
export async function paste(locator: Locator, clipboardData: JSHandle<DataTransfer>): Promise<void> {
  await locator.evaluate((element, clipboardData) => {
    const event = new ClipboardEvent('paste', { bubbles: true, cancelable: true, clipboardData });
    window.getSelection()?.anchorNode?.dispatchEvent(event);
  }, clipboardData);
}

segevfiner avatar May 08 '23 15:05 segevfiner

Wrote the following to simulate copy and paste events for testing a component that handles them:

import type { JSHandle, Locator } from '@playwright/test';

/**
 * Copy the selection to the clipboard and return a `JSHandle` to the data.
 */
export async function copy(locator: Locator): Promise<JSHandle<DataTransfer>> {
  return locator.evaluateHandle(() => {
    const event = new ClipboardEvent('copy', { bubbles: true, cancelable: true, clipboardData: new DataTransfer() });
    window.getSelection()?.anchorNode?.dispatchEvent(event);
    return event.clipboardData;
  });
}

/**
 * Paste the given data at the selection.
 */
export async function paste(locator: Locator, clipboardData: JSHandle<DataTransfer>): Promise<void> {
  await locator.evaluate((element, clipboardData) => {
    const event = new ClipboardEvent('paste', { bubbles: true, cancelable: true, clipboardData });
    window.getSelection()?.anchorNode?.dispatchEvent(event);
  }, clipboardData);
}

Work like a charm!

haven2world avatar Sep 27 '23 06:09 haven2world

It might also be useful to be able to read the current values of the clipboard, rather than e.g. using

await component.evaluate( 'navigator.clipboard.readText()',)

MattyBalaam avatar Mar 20 '24 16:03 MattyBalaam