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
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
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 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")
})
})
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
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
)
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.mdSuper quick fix:
yarn add enzyme-adapter-react-16 --dev
testHelper.js