Stable Circle CI builds with PhantomJS for larger Rails-backed frontend apps

… and check why 5600+ Rails engineers read also this

Stable Circle CI builds with PhantomJS for larger Rails-backed frontend apps

The original photo is available on stocksnap. Author: Stephen Radford.

One of the projects we work on is a rather large frontend app, built with React and backed by Rails. The app has quite a few tests and obviously we want them to run as fast as possible. We have tried a few drivers along the way but eventually we have chosen PhantomJS. So far we are pretty much happy about the choice, but it wasn’t always like that. Especially when it comes to our CI server where the tests would quite often fail randomly and prevent the app from being deployed. The random failures have been the biggest pain so far and so here are a few tricks that have helped us keep the build green.

Make sure you wait for ajax

Our app is a typical frontend application, which means there are AJAX requests sent all over the place. Even the simplest edit and save operation sends one and then shows a flash message when it’s done. Now to have a test that checks if the proper flash message is visible, we need to wait for AJAX, it’s not enough to simply do:


expect(actor).to see "Success messaage"

even though “Capybara is ridiculously good at waiting for content”. In our case this fails from time to time and so we have resorted to a custom wait_for_ajax helper method that checks if there are any AJAX requests still running:


# snippet of spec/support/helpers.rb
def wait_for_ajax
  wait_until do
    page.evaluate_script('jQuery.active').zero?
  end
end

Then in our tests we call it after clicking a Save button:


# snippet of an acceptance test
def set_reward_attribute(actor, reward)
  actor.fill_in "reward", with: reward
  actor.click_on 'Save'
  actor.wait_for_ajax
  expect(actor).to see_flash "Reward updated successfully."
  expect(actor).to see value
end

Consider switching parallel builds off

In our case, this one seems to be the main cause of our random failures. Switching it off has brought the build back to its green color and random failures are a very rare thing now. The downside is that the tests take much longer to run but it’s pretty much guaranteed that the app will be built and deployed right away without the need of rebuilding the whole thing again and again. In our worst cases, we had to do it quite a few times and already started to hate the rebuild option, knowing that it might not help and that we still have a problem somewhere else.

PhantomJS 2.0

Initially, we were using PhantomJS 1.9.8, but it didn’t have the bind method needed to support React (we had to add it ourselves). It also had some other issues, like clicking other elements than buttons or inputs. Eventually, we decided to upgrade to version 2.0 where most of the issues were eliminated. So far it has been the most stable version. Oh, and it’s slightly faster, too!

Now, to actually use PhantomJS 2.0 on CircleCI, you need to have this in your circleci.yml:


# snippet of: circleci.yml

dependencies:
  pre:
    - sudo apt-get update; sudo apt-get install libicu52
    - curl --output /home/ubuntu/bin/phantomjs-2.0.1-linux-x86_64-dynamic https://s3.amazonaws.com/circle-support-bucket/phantomjs/phantomjs-2.0.1-linux-x86_64-dynamic
    - chmod a+x /home/ubuntu/bin/phantomjs-2.0.1-linux-x86_64-dynamic
    - sudo ln -s --force /home/ubuntu/bin/phantomjs-2.0.1-linux-x86_64-dynamic /usr/local/bin/phantomjs

Use Puma instead of WEBrick

Here is a trick that may also make your build more stable. We have noticed that WEBrick, which is the default server, hangs from time to time and gives us weird timeouts during the test runs. So we searched for alternatives and ended up using Puma instead. It seems to be much more stable and here is how you can plug it in:


# fragment of: spec/spec_helper.rb
Capybara.server do |app, port|
  require 'rack/handler/puma'
  Rack::Handler::Puma.run(app, Port: port)
end

Disable animations

Our frontend uses different animations, like fading out and in. This all looks nice but obviously also makes some functions slower, and as it turns out, causes some tests to fail randomly. For tests, however, the animations are totally unnecessary, so why not turn them off? Here is how we do it.

First, we add a custom CSS class to our <body> tag, for the test environment only:


<body<%= Rails.env.test? ? ' class="disable-animations"'.html_safe : '' %>>

Then we use the following styles:


.disable-animations *,
.disable-animations *:after,
.disable-animations *:before
  transition-property: none !important
  -o-transition-property: none !important
  -moz-transition-property: none !important
  -ms-transition-property: none !important
  -webkit-transition-property: none !important
  transform: none !important
  -o-transform: none !important
  -moz-transform: none !important
  -ms-transform: none !important
  -webkit-transform: none !important
  animation: none !important
  -o-animation: none !important
  -moz-animation: none !important
  -ms-animation: none !important
  -webkit-animation: none !important

It’s one of the things that won’t hurt but may help eliminate the random test failures.

Summary

Those few tricks have helped us eliminate most the random failures and are saving us long minutes, if not hours, of rebuiling the app over and over again. We hope they can also work for you.

You might also like