At Drivy, we’ve been using Capybara and PhantomJS to run our feature specs for years. Even with its issues, PhantomJS is a great way to interact with a browser without starting a graphical interface. Recently, Chrome added support for a headless flag so it could be started without any GUI. Following this announcement, the creator of PhantomJS even announced that he would be stepping down as a maintainer.
Setting feature specs to run with a headless version of Chrome means that our features specs can be executed in the same environment most of our users are browsing with. It is also supposed to improve memory usage and stability.
Assuming you already have Chrome (59 or more recent for macOS/Linux, 60 or more recent for Windows) on your machine, you’ll also need to install ChromeDriver. On macOS, you can install it with homebrew:
brew install chromedriverIf not already present in your application, add selenium-webdriver to your Gemfile:
group :test do
gem 'selenium-webdriver'
endCapybara provides a simple API to register a custom driver. You can do so in your test/spec helper file.
Capybara.register_driver(:headless_chrome) do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: { args: %w[headless disable-gpu] }
)
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: capabilities
)
endAs stated in the documentation, the disable-gpu is needed to run Chrome as headless.
On an app running on Rails 5.1 with system test cases, use the provided DSL to use the driver:
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :headless_chrome
endOtherwise, use the more generic way of setting a javascript driver for Capybara:
Capybara.javascript_driver = :headless_chromeWith Capybara, there is a possibility to take a screenshot during your tests (or automatically on a failure). This feature results in an empty gray image on headless Chrome 59 but the proper behavior is restored on Chrome 60 (in beta as of today).
trigger methodTo prevent some issues in PhantomJS when elements would overlap, we had a lot of calls like this:
find('.clickable_element').trigger('click')In Chrome, it is raising the following error as the trigger method is not supported:
Capybara::NotSupportedByDriverError: Capybara::Driver::Node#triggerThis can now safely be replaced by the straightforward click method:
find('.clickable_element').clickYou can see an example app on drivy/rails-headless-capybara.
Even though we introduced Chrome headless very recently, we’re quite optimistic that it will lead to even less bugs in our application.