How do I access the contents of the clipboard from within a headless puppeteer test?
Asked Answered
W

4

10

I'm writing a test that uses puppeteer to test a component that copies something to the clipboard when it is interacted with. I want to test that the contents of the clipboard are correct after interacting. Other resources like this github issue mention using a tool like clipboardy to accomplish this. I tried using that and it works locally, but when run in my headless CI server, it complains about not having access to the X environment. Is there a way to access the clipboard without starting an X server?

I'm writing a test like this:

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://mypage.com');

await page.click('#my-component');

// This fails in a headless environment
expect(clipboardy.readSync()).toEqual("Some text");
Wylde answered 10/2, 2020 at 21:31 Comment(0)
W
14

By adding the 'clipboard-read' permission to puppeteer and using the Clipboard API, you can run navigator.clipboard.readText() to read from the clipboard in a test. This will work even in a headless environment:

const browser = await puppeteer.launch();
const context = browser.defaultBrowserContext();
await context.overridePermissions(/* browser origin */, ['clipboard-read']);
const page = await browser.newPage();
await page.goto('https://mypage.com');

await page.click('#my-component');

expect(await page.evaluate(() => navigator.clipboard.readText()))
  .toEqual("Some text");

Documentation of context.overridePermissions()

Wylde answered 10/2, 2020 at 21:33 Comment(5)
What is "config.APPLICATION_URL"?Alexipharmic
Thanks for self-answering, by the way, super useful :)Alexipharmic
@Alexipharmic You're welcome! Thanks for improving my answer.Wylde
@EmilyEisenberg I have a button when clicked a URL link is getting copied to the clipboard, and I need to past that in a new tab, in that case, how am I supposed to add the origin?Inbound
Where do I get the browser origin from?Pishogue
E
2

In my case I couldn't override permissions, as Eduard suggested, because it requires to give origin as a parm. I'm injecting html content in my test to the page via setContent so the page address is about:blank. Setting origin to "*" doesn't work either, nor undefined.

I have ended up mocking the clipboard api:

await page.evaluate((dataInternal) => {
    // mock clipboard
    let clipboardText = null;
    window["navigator"]["clipboard"] = {
        writeText: text => new Promise(resolve => clipboardText = text),
        readText: () => new Promise(resolve => resolve(clipboardText)),
    }
}

then you can just do the following assert:

expect(await page.evaluate(() => navigator.clipboard.readText())).toBe("your text in clipboard");
Encephalogram answered 26/10, 2021 at 20:37 Comment(1)
Does this still work? I'm getting "DOMException: No valid data on clipboard."Installation
L
1

In my chrome, navigator.clipboard = ... did not work, because navigator had getter and setter for clipboard.

Mocking clipboard:

await page.evaluate(() => {
    const clipboard = {
        async writeText(text: string) {
            this.text = text;
        },
    };
    Object.defineProperty(navigator, 'clipboard', { value: clipboard });
});

Reading clipboard content:

page.evaluate(() => navigator.clipboard.text);
Leacock answered 20/9, 2022 at 7:57 Comment(0)
L
0

In some cases:

await context.overridePermissions(/* web url */, ['clipboard-read']);

does not work instead give this:

await context.overridePermissions(/* web url */,['clipboard-read','clipboard-sanitized-write','clipboard-write']);

In my case the first code is not working, when I use this code it started to copy the data to clipboard.

Lytle answered 24/5, 2024 at 15:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.