'Emulating a clipboard copy/paste with Selinum + Capybara

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 -

  1. Copy the link using the "Copy Link" button
  2. Navigate to some other input/text field
  3. 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!



Solution 1:[1]

Note: since the original posting of this Chrome has changed the available permission types. For the headless solution go to https://chromedevtools.github.io/devtools-protocol/tot/Browser/#type-PermissionType to see what permission types are currently available


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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1