---
title: "Replacing PhantomJS with headless Chrome"
description: "Replacing PhantomJS with headless Chrome"
canonical_url: "https://www.bigbinary.com/blog/replacing-phantomjs-with-headless-chrome"
markdown_url: "https://www.bigbinary.com/blog/replacing-phantomjs-with-headless-chrome.md"
---

# Replacing PhantomJS with headless Chrome

Replacing PhantomJS with headless Chrome

- Author: Navaneeth PK
- Published: January 22, 2019
- Categories: Rails

We recently replaced PhantomJS with ChromeDriver for system tests in a project
since
[PhantomJS is no longer maintained](https://github.com/ariya/phantomjs/issues/15344).
Many modern browser features required workarounds and hacks to work on
PhantomJS. For example the `Element.trigger('click')` method does not actually
click an element but simulates a DOM click event. These workarounds meant that
code was not being tested as the code would behave in real production
environment.

#### ChromeDriver Installation & Configuration

ChromeDriver is needed to use Chrome as the browser for system tests. It can be
installed on macOS using [homebrew](https://brew.sh).

```bash
brew cask install chromedriver
```

Remove `poltergeist` from Gemfile and add `selenium-webdriver`.

```ruby
#Gemfile

- gem "poltergeist"
+ gem "selenium-webdriver"
```

Configure Capybara to use ChromeDriver by adding following snippet.

```ruby
require 'selenium-webdriver'

Capybara.register_driver(:chrome_headless) do |app|
  args = []
  args << 'headless' unless ENV['CHROME_HEADLESS']

  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOptions: { args: args }
  )

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

Capybara.default_driver = :chrome_headless

```

Above code would run tests in headless mode by default. For debugging purpose we
would like to see the actual browser. That can be easily done by executing
following command.

```ruby
CHROME_HEADLESS=false bin/rails test:system
```

After switching from Phantom.js to "headless chrome", we ran into many test
failures due to the differences in implementation of Capybara API when using
ChromeDriver. Here are solutions to some of the issues we faced.

#### 1. Element.trigger('click') does not exist

`Element.trigger('click')` simulates a DOM event to click instead of actually
clicking the element. This is a bad practice because the element might be
obscured behind another element and still trigger the click. Selenium does not
support this method, `Element.click` works as the solution but it is not a
replacement. We can replace `Element.trigger('click')` with
`Element.send_keys(:return)` or by executing javascript to trigger click event.

```ruby
#example

find('.foo-link').trigger('click')

# solutions

find('.foo-link').click

# or

find('.foo-link').send_keys(:return)

# or
# if the link is not visible or is overlapped by another element

execute_script("$('.foo-link').click();")

```

### 2. Element is not visible to click

When we switched to `Element.click`, some tests were failing because the element
was not visible as it was behind another element. The easiest solution to fix
these failing test was using `Element.send_keys(:return)` but purpose of the
test is to simulate a real user clicking the element. So we had to make sure the
element is visible. We fixed the UI issues which prevented the element from
being visible.

#### 3. Setting value of hidden fields do not work

When we try to set the value of a hidden input field using the `set` method of
an element, Capybara throws a `element not interactable` error.

```ruby
#example
find(".foo-field", visible: false).set("some text")
#Error: element not interactable

#solution
page.execute_script('$(".foo-field").val("some text")')
```

#### 4. Element.visible? returns false if the element is empty

[`ignore_hidden_elements`](https://www.rubydoc.info/gems/capybara/Capybara%2FSessionConfig:ignore_hidden_elements)
option of Capybara is `false` by default. If `ignore_hidden_elements` is `true`,
Capybara will find elements which are only visible on the page. Let's say we
have `<div class="empty-element"></div>` on our page.
`find(".empty-element").visible?` returns `false` because selenium considers
empty elements as invisible. This issue can be resolved by using
`visible: :any`.

```ruby

#example

#ignore hidden elements
Capybara.ignore_hidden_elements = true

find(".empty-element").visible?
# returns false

#solution
find('.empty-element', visible: :any)

#or

find('.empty-element', visible: :all)

#or

find('.empty-element', visible: false)

```

## Links

- [Human page](https://www.bigbinary.com/blog/replacing-phantomjs-with-headless-chrome)
