Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] a dedicated clipboard API #15860

Open
aslushnikov opened this issue Jul 21, 2022 · 8 comments
Open

[Feature] a dedicated clipboard API #15860

aslushnikov opened this issue Jul 21, 2022 · 8 comments

Comments

@aslushnikov
Copy link
Collaborator

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);
@nikolay-yavorovskiy
Copy link

Would be great.

@hxnir
Copy link

hxnir commented Dec 18, 2022

Would really help us out as well!

@cscheffauer
Copy link

+1

@RenderMichael
Copy link
Contributor

RenderMichael commented Jan 18, 2023

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.

@segevfiner
Copy link

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);
}

@haven2world
Copy link

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!

@MattyBalaam
Copy link

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()',)

@Georgegriff
Copy link
Contributor

Georgegriff commented Sep 12, 2024

Something I just found: When working with custom mime type data, such as data from an external application you cannot use navigator.clipboard.write for security reasons.

You might also find the paste events do not work because some fields, such as rich text fields with contenteditable listen to beforeinput instead. You can take a similar approach to @segevfiner with that event

const dataTransfer = new DataTransfer();
dataTransfer.setData("application/my-custom-type", "data-string");
const event = new InputEvent("beforeinput", {
        bubbles: true,
        cancelable: true,
        dataTransfer: dataTransfer,
        inputType: "insertFromPaste",
      });
element.dispatchEvent(event);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants