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

Using Capybara with headless chrome #1860

Closed
twalpole opened this issue May 3, 2017 · 77 comments
Closed

Using Capybara with headless chrome #1860

twalpole opened this issue May 3, 2017 · 77 comments

Comments

@twalpole
Copy link
Member

twalpole commented May 3, 2017

There are currently 2 issues with using Capybara with headless chrome -

(Session info: headless chrome=60.0.3080.5)
(Driver info: chromedriver=2.29.461585

  1. Headless chrome appears not to support JS system modals ( alert, confirm, prompt)
    There is a workaround for this currently in testing

  2. Attempting to close a window raises a timeout error "failed to close window in 20 seconds" and doesn't close the window
    I can't think of any way to work around this issue, so window management won't really work until this is fixed in either chromedriver or chrome.

@twalpole
Copy link
Member Author

twalpole commented May 3, 2017

The workaround for JS system modals is PR #1859 which was merged into master - 12c1005

@jesperronn
Copy link

Could you please elaborate on how I set up and run with headless chrome?

I am curious to how you tested, and I'm eager to test myself

@twalpole
Copy link
Member Author

twalpole commented May 31, 2017

@jesperronn The setup Capybara uses for it's headless chrome tests is https://github.com/teamcapybara/capybara/blob/master/spec/selenium_spec_chrome.rb#L6

You should just need to pass args: ['headless'] as an option in your driver registration

@gregsadetsky
Copy link

gregsadetsky commented Jun 6, 2017

Hi,

I'm not involved in / don't use capybara (although I've heard great things) :-) but I did run into the same issues with headless Chrome while using Selenium using Python (alerts not being supported & driver.close() causing an issue)

Notes on how I resolved both:

One difference is that we used a random variable name/value (in our single page app, setting a global "alert() has been called" variable could be a false positive as the 'true' value could be read a 2nd, 3rd, etc. time)

The second difference is that we ended up setting a cookie in the window.alert handler instead of setting a global variable -- the reason being, if you have an alert() call closely followed by a location.href change, the variable used to track if the alert happened or not will be lost; this won't happen with a cookie (we did use a random value for the cookie, for reasons explained above).

  • As for driver.close() causing "failed to close window in 20 seconds" errors, the solution was simpler: instead of using the 'stable' Chrome channel, we switched to the 'beta' channel. The current Linux version of Chrome Beta is 59.0.3071.83.

Cheers!

@twalpole
Copy link
Member Author

twalpole commented Jun 6, 2017

@gregsadetsky Thanks for the info. The alert/prompt/confirm workaround was meant to be an easy solution until Chrome/chromedriver fixed the issue, however it looks like I will need to make it more robust since Chrome 59 has released with the issue still there.

@twalpole
Copy link
Member Author

twalpole commented Jun 6, 2017

@gregsadetsky Hmmm, I still see the window errors on MacOS with Chrome 59.0.3071.86 and chromedriver 2.29.461585 so it may be fixed in linux, but it's not fully fixed.

@twalpole
Copy link
Member Author

twalpole commented Jun 6, 2017

@gregsadetsky and on linux (travis) we're seeing a different error now -

Selenium::WebDriver::Error::UnknownError:
unknown error: cannot get automation extension
from unknown error: page could not be found: chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html
(Session info: headless chrome=59.0.3071.86)
(Driver info: chromedriver=2.29.461571 (8a88bbe0775e2a23afda0ceaf2ef7ee74e822cc5),platform=Linux 4.4.0-51-generic x86_64)

Any experience with that error?

@gregsadetsky
Copy link

Unfortunately, no. It seems like that version (59.0.3071.86) will be rolling out to the stable channel (we're successfully using 59.0.3071.83 on the beta channel). Ugh. :-)

@twalpole
Copy link
Member Author

twalpole commented Jun 6, 2017

@gregsadetsky "Ugh", yeah. Wrt your comments about random variable names, after taking a quick look at my implementation again, I don't think it applies to Capybara. I create a new "modal handler" instance every time the user tells us there is going to be an alert/prompt/confirm and then remove it from the queue when the status is checked so it's not really possible for the same status to be used multiple times. The issue with a page change is valid, but swapping to cookies could also have an issue if cookies are cleared during the page change (or the new url is a different sub/domain) so I think I'll stay with the small risk of a failure if a page change occurs, for now, and hope chrome/chromedriver fix the issue soon.

@openscript
Copy link

Here is the new chromedriver 2.30. It doesn't have release notes yet.
https://chromedriver.storage.googleapis.com/index.html?path=2.30/

@jeremy
Copy link

jeremy commented Jun 8, 2017

Note that window sizing and positioning do not work with headless as of chromedriver 2.30 + Chrome 59.

From this chromedriver issue:

As part of the work we're doing to make Headless Chrome work with ChromeDriver, we're replacing the ChromeDriver automation extension with DevTools commands to control the window size. This should make the extension unnecessary, so we won't have to worry about whitelists.

If you're doing something like:

driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(1400, 1400)

Then you'll get an error like:

Selenium::WebDriver::Error::UnknownError: unknown error: cannot get automation extension
from unknown error: page could not be found: chrome-extension://aapnijgdinlhnhlmodcfapnahmbfebeb/_generated_background_page.html
  (Session info: headless chrome=59.0.3071.86)
  (Driver info: chromedriver=2.30.477690 (c53f4ad87510ee97b5c3425a14c0e79780cdf262),platform=Mac OS X 10.13.0 x86_64)

Comment out the window size/position setting to work around it.

@twalpole
Copy link
Member Author

twalpole commented Jun 8, 2017

chromedriver 2.30 fixed the issues around window closing, but all content in extra windows opened is reported as not displayed by selenium, so multiple windows are still not really usable with headless.

@nertzy
Copy link

nertzy commented Jun 12, 2017

chrome? && ((@processed_options[:desired_capabilities][:chrome_options] || {})['args'] || []).include?("headless")

This line makes a lot of assumptions about the hash structure of the Capabilities object.

I'm on a project where we have this driver defined:

Capybara.register_driver :chrome_headless do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOptions: {
      args: %w[ no-sandbox headless disable-gpu ]
    }
  )

  Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: capabilities)
end

And page.driver.headless_chrome? returns false because we have :chromeOptions instead of :chrome_options and we have :args instead of 'args'.

But Chrome does launch headlessly so it's clear that the browser launcher is being more lenient in its hash parsing.

@twalpole
Copy link
Member Author

twalpole commented Jun 12, 2017

@nertzy Yes it does, and if you'd like to propose a clean way of detecting it a PR would be appreciated. The fact that we even have to care whether it's headless or not is a hack at the moment, and hopefully modals and window interactions will actually be supported by Chrome in the near future, so we don't have to care.

@iggant
Copy link

iggant commented Jun 16, 2017

  1. client authentication client addle to access dashboard with authentication
    Failure/Error: fill_in 'user[email]', with: client.user.email
    Selenium::WebDriver::Error::UnknownError:
    unknown error: an X display is required for keycode conversions, consider using Xvfb
    (Session info: headless chrome=59.0.3071.86)
    (Driver info: chromedriver=2.30.477691 (6ee44a7247c639c0703f291d320bdf05c1531b57),platform=Linux 4.9.24-coreos x86_64)
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/response.rb:32:in `initialize'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/http/common.rb:83:in `new'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/http/common.rb:83:in `create_response'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/http/default.rb:107:in `request'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/http/common.rb:61:in `call'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/bridge.rb:170:in `execute'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/oss/bridge.rb:579:in `execute'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/remote/oss/bridge.rb:372:in `send_keys_to_element'
     # /usr/local/bundle/gems/selenium-webdriver-3.4.3/lib/selenium/webdriver/common/element.rb:156:in `send_keys'
     # /usr/local/bundle/gems/capybara-2.14.3/lib/capybara/selenium/node.rb:74:in `set'
     # /usr/local/bundle/gems/capybara-2.14.3/lib/capybara/node/element.rb:108:in `block in set'
     # /usr/local/bundle/gems/capybara-2.14.3/lib/capybara/node/base.rb:85:in `synchronize'
     # /usr/local/bundle/gems/capybara-2.14.3/lib/capybara/node/element.rb:106:in `set'
     # /usr/local/bundle/gems/capybara-2.14.3/lib/capybara/node/actions.rb:92:in `fill_in'
     # /usr/local/bundle/gems/capybara-2.14.3/lib/capybara/session.rb:784:in `block (2 levels) in <class:Session>'
     # /usr/local/bundle/gems/capybara-2.14.3/lib/capybara/dsl.rb:50:in `block (2 levels) in <module:DSL>'
     # ./spec/features/client_authentication_spec.rb:19:in `block (2 levels) in <top (required)>'

@twalpole
Copy link
Member Author

twalpole commented Jun 16, 2017

@iggant That would be a Chrome issue, not a Capybara issue.

@deivid-rodriguez
Copy link
Contributor

This one, maybe: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1772

@twalpole
Copy link
Member Author

@deivid-rodriguez Exactly that one :)

@frewsxcv
Copy link

This one, maybe: https://bugs.chromium.org/p/chromedriver/issues/detail?id=1772

Looks like this has been fixed and is just waiting for a new chromedriver release.

@lucascaton
Copy link

lucascaton commented Jun 27, 2017

I'm able to run it and wrote a blog post about it:

How to run your feature specs using Capybara and Headless Chrome

@twalpole
Copy link
Member Author

twalpole commented Jun 27, 2017

@lucascaton You've fixed what? Without the next release of chromedriver (2.31) it's not possible to run without an X server installed on linux, anything to do with multiple windows or window resizing is still pretty broken until a future release of chrome and/or chromedriver, and we're still hacking around the lack of JS modal support.

@lucascaton
Copy link

@twalpole I've been using it with chromedriver 2.30 and works perfectly, even on Circle CI, running the same version 😄

@twalpole
Copy link
Member Author

twalpole commented Jun 27, 2017

@lucascaton Yes, because Circle CI installs an X server, your tests aren't resizing windows or opening multiple windows, and Capybara is hacking around the JS modals. So it's working perfectly for you because you're not using any of the currently broken parts. That's not fixing things, that's just avoiding the cracks :) Capybara has been running its own tests with headless chrome on travis for a few weeks now, and as long as we skip all the broken tests then it's perfect.

@himankarbn
Copy link

himankarbn commented Jun 29, 2017

We are using Chromedriver 2.30 and the only issue we are facing is the resizing. Since our test suite does lot of resizing ( desktop, mobile, tablet sizes ) in a single feature spec, we are currently blocked in using headless feature.
@jeremy @twalpole can you guys suggest how to use DevTools commands to control the window size during a test?

@twalpole
Copy link
Member Author

twalpole commented Jun 29, 2017

@himankarbn Since there is no connection to send random DevTools commands over I believe this isn't possible to do at the moment. Basically, it's a waiting game until chromedriver/chrome implement/fix support.

@egbert
Copy link

egbert commented Jul 3, 2017

If you need to resize just once you can set a flag for the window size instead of resizing the window:

Capybara.register_driver :chrome_headless do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOptions: {
      args: %w[ no-sandbox headless disable-gpu window-size=1280,1024]
    }
  )

  Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: capabilities)
end

@rilian
Copy link

rilian commented Jul 4, 2017

we have sacrificed alerts functionality by disabling them with the following code, that is injected during tests:

window.alert = function() { return true; }
window.confirm = function() { return true; }

for everything else headless chrome works well.

@bbuchalter
Copy link

@rilian Alert functionality should be working with the latest Capybara and headless chrome - if it's not please let us know exactly what you're doing that doesn't work.

accept_alert works as expected in Chrome headed, but Capybara::ModalNotFound is raised in headless. Versions:

capybara 2.15.1
Google Chrome 59.0.3071.115
ChromeDriver 2.31.488763 (092de99f48a300323ecf8c2a4e2e7cab51de5ba8)

Will try upgrading to Chrome 60...

@twalpole
Copy link
Member Author

twalpole commented Aug 14, 2017

@bbuchalter What version of seleniuv-webdriver are you using (make sure a recent one) and what are you specifying in your register driver block? Also show the code you're using with accept_alert - you have to be passing the block that triggers the system modal.

@bbuchalter
Copy link

bbuchalter commented Aug 14, 2017

@twalpole

What version of seleniuv-webdriver are you using (make sure a recent one)

selenium-webdriver (3.4.4)

and what are you specifying in your register driver block?

chrome_options = [
  "window-size=#{REQUEST_SPEC_BROWSER_WIDTH},#{REQUEST_SPEC_BROWSER_HEIGHT}"
]
if ENV['HEADLESS']
  chrome_options += [
    "headless",
    "disable-gpu",
  ]
end
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
  chromeOptions: { args: chrome_options }
)
Capybara.register_driver :chrome do |app|
  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    desired_capabilities: capabilities
  )
end
Capybara.javascript_driver = :chrome

Will update selenium-webdriver and try again...

@twalpole
Copy link
Member Author

@bbuchalter and the code that calls accept_alert ?

@bbuchalter
Copy link

OK, after upgrading all the things (versions below), I now get failures for both headed and headless versions, but different errors.

Headed Chrome alert error


          Selenium::WebDriver::Error::UnhandledAlertError:
            unexpected alert open: {Alert text : Are you sure?}
              (Session info: chrome=60.0.3112.101)
              (Driver info: chromedriver=2.31.488774 (7e15618d1bf16df8bf0ecf2914ed1964a387ba0b),platform=Mac OS X 10.12.6 x86_64)

Headless Chrome alert error

     Failure/Error: accept_alert

     LocalJumpError:
       no block given (yield)

I know the docs for accept_alert say it accepts a block, but click_on "OK" just results in Capybara::ElementNotFound: Unable to find visible link or button "OK", which makes sense since this is a system dialog, not an element.

Versions

ruby 2.2.7p470 (2017-03-28 revision 58194) [x86_64-linux]
node: v8.1.2
yarn: 0.24.6
Google Chrome 60.0.3112.101 
ChromeDriver 2.31.488763 (092de99f48a300323ecf8c2a4e2e7cab51de5ba8)
selenium-webdriver (3.5.0)
capybara (2.15.1)

@bbuchalter
Copy link

🤦

I failed to understand the way the block is supposed to work:

accept_alert do
  find(".delete").click # thing that triggers alert
end

My apologies. Things working as expected now.

@NoHesHere
Copy link

So I am having another, yet similar issue. For me, in headless mode, it appears that js alerts are not even being rendered. If I run the code:

accept_confirm do
  element.click()
end

In headed chrome, it passes, because the modal is rendered. In headless, it fails with Capybara::ModalNotFound:, and if I run it in headless without any code to accept the alert, it fails waiting for the next element.

Has anyone else had the same problem, or have any ideas?

Version Info:

capybara: 2.15.1
selenium-webdriver: 3.5.1
ChromeDriver: 2.31

@twalpole
Copy link
Member Author

@NoHesHere Chrome in headless mode doesn't support system modals, so Capybara has to patch in some code to handle them. Unfortunately there is no nice way to detect that Chrome is in fact running in headless mode through selenium so we have to inspect the driver config to determine when we need to patch window.alert/confirm/prompt. The downside to this is there are many ways to configure selenium so it runs chrome headless, if you're specifying it in a way we don't detect then the patching doesn't occur. Look at https://github.com/teamcapybara/capybara/blob/master/lib/capybara/selenium/driver.rb#L322 and see whether the way you're configuring selenium would match that.

@NoHesHere
Copy link

@twalpole You are correct, we were not tripping the conditional. For anyone else with the same issue, we had defined our chromeOptions like this:

"chromeOptions" => { 'args' => %w{headless disable-popup-blocking no-sandbox disable-gpu window-size=1400,900} }

Changing it to:

chromeOptions: { 'args' => %w{headless disable-popup-blocking no-sandbox disable-gpu window-size=1400,900} }

fixed the issue for us.

@betorina
Copy link

betorina commented Sep 9, 2017

Found out that rails 5.1 can use headless chrome with a one liner:
Rails System Testing

require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, screen_size: [1400, 900], options: { args: %w[headless disable-gpu] }
end

Gives only a 'small' deprecation warning ('args' vs 'add_argument').

@rachel-carvalho
Copy link

rachel-carvalho commented Sep 12, 2017

I still get the error Unable to find modal dialog (Capybara::ModalNotFound) when I try to accept_alert.

Version info:

capybara: 2.15.1
selenium-webdriver: 3.5.2
ChromeDriver: 2.32

@twalpole
Copy link
Member Author

@rachel-carvalho Then you’re either using it incorrectly or you’re running into the same issue @NoHesHere had a couple of posts up

@Petercopter
Copy link

@rachel-carvalho @NoHesHere
I have the same problem, can't auto-accept alerts in headless mode. It works in 'headful' mode, but when you switch to headless, it no longer works.

This works:

Capybara.default_driver = :selenium_chrome

This doesn't

Capybara.default_driver = :selenium_chrome_headless

@twalpole
Copy link
Member Author

twalpole commented Sep 26, 2017

@Petercopter auto-accepting alerts shouldn't work -- the fact that is does in some drivers/setups is technically a bug - you should be specifying to accept or dismiss system modals. Assuming by "auto-accept" that you mean without using code like

accept_alert do
  click_button "button that opens alert"
end

If that's not what you mean then please provide an example of the code that is failing for you and the exact error message returned.

@Petercopter
Copy link

@twapole sorry for the confusion. I meant using the workaround code to auto accept the confirmations in the meantime, while we wait for chrome to work with the dialogs. I'm busy converting from poltergeist to headless chrome, I was just trying to work past the alert problem for now.

Nothing to see here, carry on! 😀

@twalpole
Copy link
Member Author

twalpole commented Sep 26, 2017

@Petercopter - using the default :selenium_chrome_headless it should work correctly with code like

accept_alert do
  click_button "button that opens alert"
end

is that working for you? If not, please post your code so I can try and figure out why - we don't like having things not work when using the Capybara provided defaults :). If you've modified the :selenium_chrome_headless registration then it's possible Capybara isn't detecting that it's headless (due to the specific way you have specified headless) and isn't patching the JS alert/confirm/prompt methods as needed.

@Petercopter
Copy link

@twalpole Alright, I feel ridiculous. It turns out that we were not using the Capybara accept_alert, instead we were using the driver's version, page.accept_confirm.

I've changed to using accept_alert in our test suite, and now I am onto the next adventure. Thank you so much for poking at the issue! 🌮 🍺

@twalpole
Copy link
Member Author

twalpole commented Sep 27, 2017

@Petercopter No problem, glad it's working (just to note - if it is actually a confirm modal as opposed to an alert you should use Capybaras accept_confirm instead of accept_alert - There's really no difference when using Selenium but could be in other drivers)

@rachel-carvalho
Copy link

I figured what the problem was, thanks @Petercopter! My tests were like so:

click button 'Will confirm'
accept_alert

And that was working with :selenium_chrome and firefox before that. It seems that using accept_alert or accept_confirm doesn't make much of a difference, but I changed it to accept_confirm anyway because it's the right type of dialog.

But obviously my problem was that the action that was causing the confirm to appear happened before accepting, but not within a block. Anyway, now it's all working 🎉

@jdelStrother
Copy link
Contributor

jdelStrother commented Sep 30, 2017 via email

@twalpole
Copy link
Member Author

twalpole commented Sep 30, 2017

@jdelStrother It's already in master - ec4d32f - it has been a while since a release so I'll see if I can get to one this weekend, just need to decide if it can be 2.15.2 or needs to be 2.16

@RKushnir
Copy link

@maschwenk Have you managed to figure out why hover wasn't working? Seems like the question got lost in this thread. For me it's working in regular chrome, but not in headless.

@RKushnir
Copy link

I figured you need to click anything on the page before hovering. Maybe, otherwise the window is not active.

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

No branches or pull requests