Skip to main content

Testing JavaScript partials with Rails View Specs

Blog post   •   Jan 13, 2020 13:15 CET

At Mynewsdesk we use Rails in the backend and React in combination with Redux in the frontend in our main application that our customers use to create and distribute press releases. Rails is responsible for rendering the HTML layout that hosts the React app. This layout file includes a partial that contains various JavaScript variables that we're passing from Rails to React.

When we develop new features, we often put them behind feature flags which are stored in Redis by the flipper gem. Since many feature flags are included in the partial mentioned above, this file is often changed by both frontend and backend developers.

Feature flags that are not set correctly can lead to unexpected bugs in production. To prevent accidental regressions when making changes in the partial we recently added view specs for the partial to ensure two things:

1. The partial should render without any unhandled Ruby exceptions.

In RSpec the view spec might look like this:

describe 'layouts/_env.html.erb' do
  it 'renders'
    expect { render }.not_to raise_error
  end
end

This will catch bugs like undefined local variable or method or undefined method `foo' for nil:NilClass, as sometimes introduced by typos, syntax errors or faulty MacBook Pro keyboards.

2. The partial should contain the expected JavaScript objects which parse without errors in a JavaScript runtime.

We could use expect(rendered).to have_content to look for JavaScript variables that we expect Rails to render. However the matcher have_content is string-based and cannot guarantee that the JavaScript object we're rendering is actually valid JavaScript.

To check for valid JavaScript we use the MiniRacer gem which gives us a V8-based JavaScript context that we can use to evaluate the JavaScript object and compare against a Ruby hash.

let(:js_context) do
  render
  snapshot = MiniRacer::Snapshot.new(rendered)
  MiniRacer::Context.new(snapshot: snapshot)
end

it 'renders feature flags' do
  mnd = js_context.eval("MND")
  expect(mnd["featureFlags"]).to include(
    "featureA" => true,
    "featureB" => false,
  )
end

These specs help us to make changes to our JavaScript partial with confidence.