browser testing: enable (or make it easier) to copy/paste text
Clear and concise description of the problem
I want to paste text into an element
Suggested solution
no idea 😅 I'm sure the drivers must have support for the clipboard api somehow
Alternative
No response
Additional context
No response
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
no idea 😅 I'm sure the drivers must have support for the clipboard api somehow
I'm not sure. There are some issue on playwright https://github.com/microsoft/playwright/issues/15860 https://github.com/microsoft/playwright/issues/24039, which suggest workaround using ClipboardEvent or navigator.clipboard on window (so no cdp or bid based mechanism I suppose?)
Have you tried testing-library's clipboard api? https://testing-library.com/docs/user-event/clipboard
yeah I tried RTL with
await userEventTestingLibrary.keyboard('{Control}{c}');
await userEventTestingLibrary.keyboard('{Control}{v}');
and also
await userEventTestingLibrary.paste('test');
but it didn't work
to be clear, I don't want to interact with window.navigator.clipboard. I need to test what happens when you paste into a specific text input
Once again, it looks working with this example of mine :sweat_smile: https://stackblitz.com/edit/vitest-dev-vitest-ccmzqe?file=test%2Frepro.test.ts (please test it locally to use playwright) Note that {Control>} is the command to hold down the key.
Screenshot
import { test } from 'vitest';
import { page, userEvent } from '@vitest/browser/context';
test('clipboard', async () => {
document.body.innerHTML = `
<input placeholder="first" />
<input placeholder="second" />
`;
// write "hello" and copy to clipboard
await userEvent.click(page.getByPlaceholder('first'));
await userEvent.keyboard('hello');
await userEvent.keyboard('{Control>}{a}{/Control}');
await userEvent.keyboard('{Control>}{c}{/Control}');
// paste twice into second
await userEvent.click(page.getByPlaceholder('second'));
await userEvent.keyboard('{Control>}{v}{/Control}');
await userEvent.keyboard('{Control>}{v}{/Control}');
// hellohello
console.log(page.getByPlaceholder('second').query()?.value);
});
(Tested with playwright chromium and webdriverio chrome)
Can you share how you code looks like?
"Control+C/X/V" combo seems to mostly work on playwright https://github.com/microsoft/playwright/issues/8114 and webdriverio https://webdriver.io/docs/api/expect-webdriverio/#tohaveclipboardtext, so we could implement testling-library-like copy/cut/paste API on top of that https://testing-library.com/docs/user-event/clipboard/
I'm not sure how we can expose DataTransfer though.
thanks for the repro, but it seems like it's not working?
the value is not there and nothing is logged to the console.
it also doesn't work locally
Hmm weird, It's working on my Linux PC. Do you use Mac? According to https://github.com/microsoft/playwright/issues/8114, you might need Meta instead of Control for modifier.
The copy/paste userEvent API is supposed to be implemented at some point: https://github.com/vitest-dev/vitest/issues/5770#issuecomment-2175871591
PRs are welcome.
after my conversation in discord with @hi-ogawa, we figured out the issue:
- when you run in stackblitz, it always run in 'preview' (even if you configured playwright or webdriverio)
- so I had to test it locally, but since I'm using a Mac, I have to use the
Metakey and notControl
For other people with the same issue, this is how you copy/paste content:
Mac
import { test } from 'vitest';
import { page, userEvent } from '@vitest/browser/context';
test('clipboard', async () => {
document.body.innerHTML = `
<input placeholder="first" />
<input placeholder="second" />
`;
// write "hello" and copy to clipboard
await userEvent.click(page.getByPlaceholder('first'));
await userEvent.keyboard('hello');
await userEvent.keyboard('{selectall}');
await userEvent.keyboard('{Meta>}{c}{/Meta}');
// paste twice into second
await userEvent.click(page.getByPlaceholder('second'));
await userEvent.keyboard('{Meta>}{v}{/Meta}');
await userEvent.keyboard('{Meta>}{v}{/Meta}');
// hellohello
console.log(page.getByPlaceholder('second').query()?.value);
});
Linux/Windows
import { test } from 'vitest';
import { page, userEvent } from '@vitest/browser/context';
test('clipboard', async () => {
document.body.innerHTML = `
<input placeholder="first" />
<input placeholder="second" />
`;
// write "hello" and copy to clipboard
await userEvent.click(page.getByPlaceholder('first'));
await userEvent.keyboard('hello');
await userEvent.keyboard('{selectall}');
await userEvent.keyboard('{Control>}{c}{/Control}');
// paste twice into second
await userEvent.click(page.getByPlaceholder('second'));
await userEvent.keyboard('{Control>}{v}{/Control}');
await userEvent.keyboard('{Control>}{v}{/Control}');
// hellohello
console.log(page.getByPlaceholder('second').query()?.value);
});
oh, nevermind. Playwright supports the ControlOrMeta key natively, so you can use just that 😅
https://playwright.dev/docs/api/class-keyboard#keyboard-press
import { test } from 'vitest';
import { page, userEvent } from '@vitest/browser/context';
test('clipboard', async () => {
document.body.innerHTML = `
<input placeholder="first" />
<input placeholder="second" />
`;
// write "hello" and copy to clipboard
await userEvent.click(page.getByPlaceholder('first'));
await userEvent.keyboard('hello');
await userEvent.keyboard('{selectall}');
await userEvent.keyboard('{ControlOrMeta>}{c}{/ControlOrMeta}');
// paste twice into second
await userEvent.click(page.getByPlaceholder('second'));
await userEvent.keyboard('{ControlOrMeta>}{v}{/ControlOrMeta}');
await userEvent.keyboard('{ControlOrMeta>}{v}{/ControlOrMeta}');
// hellohello
console.log(page.getByPlaceholder('second').query()?.value);
});