I’ve been using Capybara-WebKit for a long time but while switching from CircleCI 1.0 to CircleCI 2.0 I had some problems to use it on the CI.

This triggered to try Chrome Headless with Selenium Webdriver. I will show you how to configure Circle CI 2.0 and your Ruby on Rails project to use capybara/selenium/chrome headless together.

Add capybara and selenium-webdriver

Let’s add capybara and selenium-webdriver gems to Gemfile:

# Gemfile
group :development, :test do
  gem 'capybara'
  gem 'selenium-webdriver'
end

and run bundle install.

If you already had the gems in your Gemfile then ensure you have latest version with bundle update capybara selenium-webdriver.

If you want to make sure Capybara feature specs will work on your development machine:

$ brew install chromedriver

If your feature specs fail then upgrade the driver because you may have installed old one.

$ brew upgrade chromedriver

Configure Capybara

Add config file for Capybara:

# spec/support/config/capybara.rb
JS_DRIVER = :selenium_chrome_headless

Capybara.default_driver = :rack_test
Capybara.javascript_driver = JS_DRIVER
Capybara.default_max_wait_time = 2

RSpec.configure do |config|
  config.before(:each) do |example|
    Capybara.current_driver = JS_DRIVER if example.metadata[:js]
    Capybara.current_driver = :selenium if example.metadata[:selenium]
    Capybara.current_driver = :selenium_chrome if example.metadata[:selenium_chrome]
  end

  config.after(:each) do
    Capybara.use_default_driver
  end
end

Ensure you load config files from spec/support directory:

# spec/rails_helper.rb

# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

Example feature spec

We can create example feature spec to test if everything works:

# spec/features/home_spec.rb
feature 'Homepage Features' do
  before { visit root_path }

  # it won't run js code but it is fast
  it do
    expect(page).to have_content 'Hello World'
  end

  # it will run js code
  it '', :js do
    expect(page).to have_content 'Hello World'
  end

  # it will open Firefox
  # remove x from xit to run the test in Firefox on your machine to preview
  xit '', :selenium do
    expect(page).to have_content 'Hello World'
  end

  # it will open Chrome
  # remove x from xit to run the test in Chrome on your machine to preview
  xit '', :selenium_chrome do
    expect(page).to have_content 'Hello World'
  end
end

And run tests on your development machine with bin/rspec spec/features/home_spec.rb or bundle exec rspec spec/features/home_spec.rb.

Configure CircleCI 2.0 to run Chrome headless

Here is example .circleci/config.yml:

# .circleci/config.yml
version: 2
jobs:
  build:
    parallelism: 1
    working_directory: ~/project-name
    docker:
      # this is important to use proper image with browsers support
      - image: circleci/ruby:2.4.2-node-browsers
        environment:
          PGHOST: 127.0.0.1
          PGUSER: project-name
          RAILS_ENV: test
      - image: circleci/postgres:9.4.12-alpine
        environment:
          POSTGRES_DB: project-name_test
          POSTGRES_PASSWORD: ""
          POSTGRES_USER: project-name
      - image: redis:3.2.7
    steps:
      - checkout

      # Restore bundle cache
      - type: cache-restore
        # remove space between { {
        key: project-name-{ { checksum "Gemfile.lock" }}

      # Bundle install dependencies
      - run: bundle install --path vendor/bundle

      # Store bundle cache
      - type: cache-save
        # remove space between { {
        key: project-name-{ { checksum "Gemfile.lock" }}
        paths:
          - vendor/bundle

      # Prepare .env, useful if you use dotenv gem
      - run: cp .env.example .env

      # Database setup
      - run: bundle exec rake db:create
      - run: bundle exec rake db:schema:load

      # Run rspec in parallel
      - type: shell
        command: |
          bundle exec rspec --profile 10 \
                            --format RspecJunitFormatter \
                            --out /tmp/test-results/rspec.xml \
                            --format progress

      # Save artifacts
      - type: store_test_results
        path: /tmp/test-results

Speed up your tests with Circle CI parallelisation

If your feature specs are very long you can save some time by running multiple parallel CI nodes. For instance set it to 6 in .circleci/config.yml and use dynamic RSpec specs allocation across CI nodes with knapsack_pro gem and Queue Mode to get optimal test suite split to save as much time as possible.

# .circleci/config.yml
jobs:
  build:
    parallelism: 6

  steps:
    # some tests that are not balanced and executed only on first CI node
    - run: case $CIRCLE_NODE_INDEX in 0) npm test ;; esac

    # auto-balancing CI build time execution to be flat and optimal (as fast as possible).
    # Queue Mode does dynamic tests allocation so the previous not balanced run command won't
    # create a bottleneck on the CI node
    - run:
      name: RSpec via knapsack_pro Queue Mode
      command: |
        # export word is important here!
        export RAILS_ENV=test
        bundle exec rake "knapsack_pro:queue:rspec[--format documentation]"

You can learn how RSpec test suite parallelisation works in 1 minute video.

Summary

Now you are good to push your code to a repository and see how your Capybara feature specs work with Chrome Headless on CircleCI 2.0.