问题
I have a "Copy Link" button in my frontend UI. When clicked, the URL inside an input box is copied to the user's clipboard with this JS:
const copyTextarea = document.querySelector("#copy-link-button");
copyTextarea.focus();
copyTextarea.select();
document.execCommand('copy');
When I try it out locally, this functionality works perfectly so I know the feature itself is working correctly.
However I'm unable to test the copy with Capybara. I know from this post that Capybara does not provide a clipboard API, but my workaround is to -
- Copy the link using the "Copy Link" button
- Navigate to some other input/text field
- Paste into the field with CTRL+V and read the contents of the field to verify
My test:
# Copy the link
page.find("#copy-link-button").click
wait_for_ajax
# Visit some other page that I know has an input/text field
visit account_settings_path
input = page.find("#user_email")
# Clear the field
fill_in("user[email]", with: "")
# Paste in the contents of the clipboard
input.base.send_keys([:control, "v"])
# Validate
expect(input.value).to eq("some value");
However nothing gets pasted into that input (input.value
and input.text
return ""
).
Is this a valid approach? Is the issue here with copying the text in the first place or did I make an error in pasting the data?
Thanks!
回答1:
There are lots of security rules around access to the clipboard content, so trying to get paste working from Capybara is really going to be an exercise in frustration. Additionally most browsers won't actually do anything when ctrl/command v is sent as a keystroke since it's a system triggered action not the browser.
However, since you just want to verify the copy works you don't actually need to trigger the paste, you just need to get around the permission requirements to use the clipboard API. If you're using Chrome (in a non-headless configuration - doesn't currently work headless) you can do that by setting the profile.content_settings.exceptions.clipboard
preference in your driver registration
Capybara.register_driver :chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new
options.add_preference('profile.content_settings.exceptions.clipboard', {
'*': {'setting': 1}
})
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
If you are running in headless mode and using up-to-date Capybara and selenium with Chrome another option is to use CDP to grant the permissions
page.driver.browser.execute_cdp('Browser.grantPermissions', origin: page.server_url, permissions: ['clipboardRead', 'clipboardWrite'])
Once you have the permissions worked out you can then use evaluate_async_script
to access the clipboard data
clip_text = page.evaluate_async_script('navigator.clipboard.readText().then(arguments[0])')
Notes:
1. there is no need to use base
to call send_keys
on an element, just call it on the element
2. expect(input.value).to eq("some value")
will lead to flaky tests, instead you should use the Capybara provided matchers like expect(page).to have_field(with: 'some value')
or expect(input).to match_selector(:field, with: 'some value')
来源:https://stackoverflow.com/questions/58034127/emulating-a-clipboard-copy-paste-with-selinum-capybara