Skip to content

Instantly share code, notes, and snippets.

@andrewprogers
Last active June 11, 2024 22:08
Show Gist options
  • 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)

@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