Skip to content

Instantly share code, notes, and snippets.

@andrewprogers
Last active October 9, 2018 23:26
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save andrewprogers/65f0228c262fbe8e1efe767527540aec to your computer and use it in GitHub Desktop.
Save andrewprogers/65f0228c262fbe8e1efe767527540aec to your computer and use it in GitHub Desktop.
Setting up A new React on Rails app with webpacker and full test suite

React on Rails with Test Suite (Karma, Jasmine, Enzyme, PhantomJS)

The steps included here detail the steps I followed to get a new React on Rails app set up, with a focus on testing JS components with Karma / Jasmine / Enzyme. A lot of this was liberally borrowed / modified from the Launch Academy curriculum, but there are some additional steps involved to get everything working with webpacker:

Unless otherwise specified, run the code in the terminal

Install Rails with Webpacker configured for React:

rails new project-name
cd project-name

Add to your gemfile and run bundle:

gem 'webpacker'

Install Webpacker Packages:

rake webpacker:install
rake webpacker:install:react

Installing the Test Suite

Install Karma, Jasmine, PhantomJS for running Tests:

yarn add karma --dev
yarn add karma-cli --dev
yarn add karma-jasmine --dev
yarn add jasmine-core --dev
yarn add phantomjs-prebuilt --dev
yarn add karma-phantomjs-launcher --dev

Create files:

// Source code goes here:
app/javascript/react/src

// Test code goes here:
app/javascript/react/test

// Create empty testHelper.js file, this will load your js test files:
touch app/javascript/react/test/testHelper.js

Initialize karma:

karma init

At the prompt, select jasmine for framework, then no to RequireJS Tab to select PhantomJS as an autoloaded browser. Use app/javascript/react/test/testHelper.js for the location of test files, skip the next question. Select yes to run tests on change

Make webpack play nice with Karma: See: https://github.com/webpack-contrib/karma-webpack for reference

yarn add karma-webpack --dev

And add to karma.conf.js

  preprocessors: {
    'app/javascript/react/test/testHelper.js': ['webpack']
  },
  
  ...
  // Put this section at the bottom of the config.set block to void load order issues
  // Make sure to add a comma to the end of the previous line before this.
  
  webpack: {
    module: {
      loaders: [
        {
          test: /\.jsx?/,
          exclude: /node_modules/,
          loader: 'babel-loader'
        }
      ]
    }
  }

And add the following to your testHelper.js:

import 'babel-polyfill';

import React from 'react';
import { mount } from 'enzyme';
import jasmineEnzyme from 'jasmine-enzyme';

beforeEach(() => {
  jasmineEnzyme();
})

// function to require all modules for a given context
let requireAll = requireContext => {
  requireContext.keys().forEach(requireContext);
};

// require all js files except testHelper.js in the test folder
requireAll(require.context('./', true, /^((?!testHelper).)*\.jsx?$/));

// require all js files except main.js in the src folder
requireAll(require.context('../src/', true, /^((?!main).)*\.jsx?$/));

// output to the browser's console when the tests run
console.info(`TESTS RAN AT ${new Date().toLocaleTimeString()}`);

Add Enzyme for React Component tests: See: https://github.com/airbnb/enzyme/blob/master/docs/guides/webpack.md for reference

yarn add react-addons-test-utils --dev
yarn add enzyme --dev
yarn add react-test-renderer --dev
yarn add jasmine-enzyme --dev

Update the webpack part of karma.conf.js

  webpack: {
    module: {
      loaders: [
        {
          test: /\.jsx?/,
          exclude: /node_modules/,
          loader: 'babel-loader'
        }
      ]
    },
    externals: {
      cheerio: 'window',
      'react/addons': 'react',
      'react/lib/ExecutionEnvironment': 'react',
      'react/lib/ReactContext': 'react',
      'react-addons-test-utils': 'react-dom',
    }
  }

Add your test files and run with karma start (and pray):

Example Files:

Example Component:

app/javascript/react/src/App.js
import React from 'react';

const App = props => {
  return(
    <h1>Hello World</h1>
  )
}

export default App

Example Test:

app/javascript/react/src/AppTest.js
import App from '../../src/App';
import React from 'react'
import { mount } from 'enzyme';
import jasmineEnzyme from 'jasmine-enzyme';

describe('A test for App', () => {
  let wrapper;

  beforeEach(() => {
    wrapper = mount(<App />)
  })

  it('should pass', () => {
    expect(wrapper.find('h1').text()).toEqual("Hello World")
  })
})

Set up the app

Now, get rails set up to render a react app. Create an index file that will serve as the point of entry:

touch app/javascript/packs/index.js

With the following Content:

import React from 'react';
import ReactDOM from 'react-dom';
import YourAppName from '../react/src/YourAppName';

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(<YourAppName />, document.getElementById('app'));
})

Then create a static rails page, this will need:

  • A route in routes.rb: root 'static_pages#index'
  • A StaticPagesController with an index route
  • A view template: app/views/static_pages/index.html.erb

Put a link to the index.js in the view page (or the layout) with: <%= javascript_pack_tag 'index.js' %> And include a div tag with id="app" on the page where you want your app.

How to run your app now:

  • In one tab run: ./bin/webpack-dev-server
  • In another run: rails s

Optional Odds and Ends

Enable hot module replacement! In config/webpack/development.js:

module.exports = merge(sharedConfig, {
  ...
  plugins: [
      new webpack.HotModuleReplacementPlugin() // Enable HMR
    ],
  ...
  dev Server: {
    hot: true,
    ...
  }

Make it easier to start the dev-server with foreman and a Procfile:

gem install foreman // Note that the foreman docs explicitly instruct you not to put it in your gemfile
touch Procfile

Put this in your Procfile:

web: bundle exec rails s
webpacker: ./bin/webpack-dev-server

Now you can start webpack and rails with one command: foreman start Note that you may need to visit a different port now (try localhost:5000)

@drewandre
Copy link

Hi Andrew. This has been incredibly helpful to many of us at Launch Academy. Thank you!

Would like to point out one section that is breaking. Using your boilerplate, Karma will complain about not having an adapter. Enzyme 3 had some breaking changes that now require this adapter: https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3.md

Super quick fix:

  • Add the enzyme/react adapter: yarn add enzyme-adapter-react-16 --dev
  • Add the following lines to the top of testHelper.js
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

@nickbreid
Copy link

Another tiny addition:

If you want to be able to start the webpack-dev server with yarn start instead of ./bin/webpack-dev-server you can pop into package.json and add an object of scripts like so:

"name": "project-name",
  "scripts": {
    "start": "./bin/webpack-dev-server"
  },
  . . . 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment