Wallaby and react-slingshot

React slingshot is a heavy-duty production ready React boilerplate packed with the latest JavaScript tools. The author thought a lot about testing, among other things.

But, these features introduce complexity, largely due to Webpack, which is notoriously difficult to wrangle. For this project, our Wallaby setup is significantly more complicated then in our previous examples.

It's complicated but worthwhile. Let's get started.

See the completed example app on github

See the react-slingshot source code on github.

Note: you can learn how the author designed this app if you pony up for Cory's pluralsight course. He's an excellent educator. Get it here.

Follow the react-slingshot instructions (steps 1 - 5).

The react-slingshot author, Cory House, tells us how to setup this boilerplate. Basically, we will clone the repo and run a couple npm commands. See the instructions here:

https://github.com/coryhouse/react-slingshot

Note: Wallaby does not require that we actually run the app. We only need to complete steps 1 & 2:

  1. Clone the app: git clone https://github.com/coryhouse/react-slingshot.git
  2. run: cd react-slingshot/ && npm run setup

Install ‘wallaby-webpack’

This plugin exists so that Wallaby can compile sourcecode using Webpack, similar to how we run Webpack in our development or production environments. One example, this app imports CSS files, so we need to use a Webpack CSS loader.

Wallaby needs to use a Webpack config, either the same Webpack config you use for your development, or a new Webpack config jsut for Wallaby.

In this example, we are going to add a new Webpack config just for wallaby, and it will live inside the wallaby.js config.

You can learn more about the plugin here.

Install it with an npm command:

run: npm i --save-dev wallaby-webpack

Deal with issues

If we try using a basic wallaby config, like from the simple wallaby node app, we will encounter many errors. React-Slingshot is powerful, but it is not simple. We need to configure wallaby accordingly.

Issues we will need to address:

  • Testing with Mocha

React-slingshot uses the mocha test framework and we need to let Wallaby know. We will get some Mocha helpers, namely before, which fixes the error ReferenceError: Can't find variable: before.

  • Mocking with Sinon

Sinon has issues with Webpack, so the wallaby-webpack config needs fine tuning. We need to tell wallaby-webpack where to find the Sinon package, and also tell Webpack to not parse the simon module.

Learn more about Sinon's issues with Webpack here:

https://github.com/wallabyjs/public/issues/821
https://github.com/wallabyjs/public/issues/666

  • Importing CSS

Using webpack, we can import CSS files the same as JS files. But, we need to tell Webpack which loaders that the CSS files require. This fixes an error, Error: Cannot find module "../styles/about-page.css”

See some discussion around this issue here:

https://github.com/wallabyjs/public/issues/586

  • Using Enzyme

Enzyme requires react-addons-test-utils, a helper package provided by the React team, and we need to tell Webpack that this package is external. Fixes the error Cannot resolve module 'react/addons’

https://github.com/airbnb/enzyme/issues/503

We will add a line to webpack config ‘externals’, and the wallaby-webpack section of our config will resemble this:

  externals: {
    "react": "React",
    jsdom: 'window',
    cheerio: 'window',
    'react/lib/ExecutionEnvironment': true,
    'react/lib/ReactContext': 'window',
    'react/addons': true,
  },

Learn more about Webpack 'externals' here: https://webpack.github.io/docs/library-and-externals.html

  • Using Object.assign

Another error lurks, concerning the function Object.assign:

undefined is not a constructor (evaluating 'Object.assign(getAppState(), { dateModified: dateModified })’)  

If you don't know how to use Object.assign, it's a great addition to JavaScript, it helps create objects and it helps follow a functional programming style. Learn more here.

Object.assign isn’t included in PhantomJS, which Wallaby uses by default. Consider this issue.

Instead of PhantomJS, we can use electron, a sort of headless chrome. It’s a more modern alternative to PhantomJS and will fix our Object.assign issue. Learn more about electron here.

Run this command now: npm install --save-dev electron

In our config file we will tell Wallaby to run the 'electron' environment. The key & property will look like this:

env: {  
  kind: ‘electron’
}
  • Babel & react-hmre

React-slingshot uses Babel and its babel config specifies that development should use react-hmre. Wallaby interferes with this, so the easiest thing for now is to remove the plugin ‘react-hmre’ from .babelrc.

Learn more about this issue here.

Edit your .babelrc so it resembles this:

{
  "presets": [
    "latest",
    "react",
    "stage-1"
  ],
  "env": {
    "development": {
    },
    "production": {
      "plugins": [
        "transform-react-constant-elements",
        "transform-react-remove-prop-types"
      ]
    }
  }
}


// .bablerc

Create the wallaby.js config file

Create the following file, named wallaby.js:

'use strict';

let wallabyWebpack = require('wallaby-webpack');

let webpackConfig = {  
  module: {
    noParse: [
      /\/sinon\.js/
    ],
    loaders: [
      {test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel']},
      {test: /\.eot(\?v=\d+.\d+.\d+)?$/, loader: 'file'},
      {test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url?limit=10000&mimetype=application/font-woff'},
      {test: /\.[ot]tf(\?v=\d+.\d+.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'},
      {test: /\.(jpe?g|png|gif)$/i, loader: 'file?name=[name].[ext]'},
      {test: /\.ico$/, loader: 'file?name=[name].[ext]'},
      {test: /(\.css|\.scss)$/, loaders: ['style', 'css?sourceMap', 'postcss', 'sass?sourceMap']},
      {test: /\.json$/, loader: "json"}
    ]
  },
  externals: {
    // Use external version of React instead of rebuilding it
    "react": "React",
    jsdom: 'window',
    cheerio: 'window',
    'react/lib/ExecutionEnvironment': true,
    'react/lib/ReactContext': 'window',
    'react/addons': true,
  },
  resolve: {
    extensions: ['', '.js', '.jsx'],
    alias: {
     sinon: 'sinon/pkg/sinon'
   }
  }
};

module.exports = function (wallaby) {

  let webpackPostprocessor = wallabyWebpack(webpackConfig);

  return {
    files: [
      {pattern: 'node_modules/react/dist/react-with-addons.js', instrument: false},
      'src/**/*.css',
      '!src/**/*.spec.js',
      {pattern: 'src/**/*.js*', load: false}
    ],

    tests: [
      {pattern: 'src/**/*.spec.js', load: false}
    ],

    compilers: {
      '**/*.js*': wallaby.compilers.babel()
    },

    postprocessor: webpackPostprocessor,

    testFramework: 'mocha',

    env: {
      kind: 'electron'
    },

    setup: function () {
      window.__moduleBundler.loadTests();
    }
  };
};

// wallaby.js

The react-slingshot demo app includes some basic tests. You should be able to start Wallaby and see those tests pass. Going forward, you can write tests using Mocha, Enzyme, Sinon, and also enjoy the react-slingshot development experience. This is a starting point for a serious, production ready client-side web app.

If you want more practice and you haven't tried the other examples in this series, you might want to look at a simple node app or a similar project using the create-react-app package.

See the completed example app on github