Category Archives: JavaScript

React + Recompose: Calling Multiple HOC Wrappers

PROBLEM

Sometimes, wrapping a React component with multiple High Order Components (HOC) can get rather unwieldy and unreadable.

For example:-

import React from 'react';
import { withRouter } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import withWidth from 'material-ui/utils/withWidth';

class MyComponent extends React.PureComponent {
	// ...
}

export default withRouter(withStyles(styles)(withWidth()(MyComponent)));

SOLUTION

To fix this, we can leverage recompose library.

Now, we can rewrite the above example like this:-

import React from 'react';
import { withRouter } from 'react-router-dom';
import { withStyles } from 'material-ui/styles';
import withWidth from 'material-ui/utils/withWidth';
import compose from 'recompose/compose';

class MyComponent extends React.PureComponent {
	// ...
}

export default compose(
  withRouter,
  withStyles(styles),
  withWidth(),
)(MyComponent);

Keep in mind, the HOC order defined in compose(..) is important.

Advertisements

Webpack + ESLint: Automatically Fix ESLint Errors

PROBLEM

Given the following webpack.config.js

module.exports = {
  ...
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js?$/,
        loader: 'eslint-loader',
        exclude: /node_modules/,
      },
	  ...
    ],
  },
  ...
};

When running any Webpack command, ESLint may find violations and halt the entire process with the following error message:-

/path/to/front-end-stack/src/js/components/home/Home.js
  43:11  error  Expected indentation of 6 space characters but found 10  react/jsx-indent
  44:14  error  Expected indentation of 6 space characters but found 13  react/jsx-indent

x 2 problems (2 errors, 0 warnings)
  2 errors, 0 warnings potentially fixable with the `--fix` option.

SOLUTION

Certain errors (ex: trailing commas, wrong indentation, extra semicolon) are easily fixable.

There’s no need to halt the process and wait for developers to fix these obvious errors.

To configure ESLint to automatically fix these “soft” errors, add the following options block to the above rule:-

module.exports = {
  ...
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js?$/,
        loader: 'eslint-loader',
        exclude: /node_modules/,
        options: {
          fix: true,
        },		
      },
	  ...
    ],
  },
  ...
};

If you are using any VCS, remember to commit any file changes.

ES6 + Mocha + Sinon: Mocking Imported Dependency

PROBLEM

Let’s assume we have the following 2 files:-

apis.js

import fetch from 'isomorphic-fetch';

export const logout = () => (
  fetch('/logout')
    .then(resp => resp.json())
    .catch(err => err)
);

service.js

import { logout } from './apis';

export const kickUserOut = activeSession => (
  activeSession ? logout() : undefined
);

Let’s assume we want to test the logic in service.js without using nock to mock the HTTP call in apis.js.

While proxyquireify allows us to mock out the apis.js dependency in service.js, sometimes it is a little more complicated than needed.

SOLUTION

A simpler approach is to use sinon to stub out logout() defined in apis.js.

service-spec.js

import { beforeEach, afterEach, describe, it } from 'mocha';
import { expect } from 'chai';
import sinon from 'sinon';
import { kickUserOut } from './service';

// import everything as an object
import * as apis from './apis';

describe('service => kickUserOut', () => {
  let logoutStub;

  // before running each test, stub out `logout()`
  beforeEach(() => {
    logoutStub = sinon.stub(apis, 'logout').returns('success');
  });

  // after running each test, restore to the original method to
  // prevent "TypeError: Attempted to wrap logout which is already wrapped"
  // error when executing subsequent specs.
  afterEach(() => {
    apis.logout.restore();
  });

  it('given active session, should invoke logout API', () => {
    expect(kickUserOut(true)).to.deep.equal('success');
    expect(logoutStub.calledOnce).to.equal(true);
  });

  it('given expired session, should not invoke logout API', () => {
    expect(kickUserOut(false)).to.equal(undefined);
    expect(logoutStub.calledOnce).to.equal(false);
  });
});

Underscore.js: Introducing _.chain(…)

PROBLEM

Let’s assume we have the following JSON data:-

[
    {
        "date": "2015-07-30",
        "calendarAppointmentPtoJsonBeans": [
            {
                "basicEmployeeJsonBean": {
                    "id": 1,
                    "name": "Vrabel"
            	}
            }
        ]
    },
    {
        "date": "2015-07-31",
        "calendarAppointmentPtoJsonBeans": [
            {
                "basicEmployeeJsonBean": {
                    "id": 2,
                    "name": "Cray"
            	}
            },
            {
                "basicEmployeeJsonBean": {
                    "id": 1,
                    "name": "Vrabel"
            	}
            },
            {
                "basicEmployeeJsonBean": {
                    "id": 3,
                    "name": "Haeflinger"
            	}
            }
        ]
    }
]

What we want to do is to get all unique employees and ordered them by their names so that we get the following data:-

[
    {
        "id": 2,
        "name": "Cray"
    },
    {
        "id": 3,
        "name": "Haeflinger"
    },
    {
        "id": 1,
        "name": "Vrabel"
    }
]

SOLUTION 1: Less Elegant

Underscore.js provides various functions that allow us to pull this off.

var employees = _.sortBy( _.unique( _.pluck( _.flatten(
                _.pluck( jsonData, 'calendarAppointmentPtoJsonBeans' ) ),
                        'basicEmployeeJsonBean' ), function ( employee ) {
                    return employee.id;
                } ), function ( employee ) {
                    return employee.name;
                } );

While doable, the code is virtually not readable.

If you hate your peers and life, this is what you would write.

SOLUTION 2: More Elegant

The good news is Underscore.js also provides _.chain(..) that allows us to do the same thing through method chaining:-

var employees = _.chain( jsonData )
                .pluck( 'calendarAppointmentPtoJsonBeans' )
                .flatten()
                .pluck( 'basicEmployeeJsonBean' )
                .unique( function ( employee ) {
                    return employee.id;
                } )
                .sortBy( function ( employee ) {
                    return employee.name;
                } )
                .value();

Karma: Managing Plugin Versions in package.json

PROBLEM

Let’s assume our package.json looks like this:-

{
  "name": "testKarma",
  "private": true,
  "devDependencies": {
    "karma": "^0.12.24",
    "karma-chrome-launcher": "^0.1.4",
    "karma-coverage": "^0.2.4",
    "karma-jasmine": "^0.2.2",
    "karma-junit-reporter": "^0.2.1",
    "karma-phantomjs-launcher": "^0.1.4"
  }
}

What we want to do is to update all the plugin versions defined in this file.

SOLUTION

After trying out several solutions, it appears that using npm-check-updates is a better and cleaner solution for discovering newer versions of these plugins.

First, we need to install npm-check-updates globally. You may need to use sudo to do so.

sudo npm install -g npm-check-updates

Once installed, run the following command within the project root directory to discover any new versions:-

npm-check-updates

Output:-

"karma-chrome-launcher" can be updated from ^0.1.4 to ^0.1.5 (Installed: 0.1.5, Latest: 0.1.5)
"karma-coverage" can be updated from ^0.2.4 to ^0.2.6 (Installed: 0.2.6, Latest: 0.2.6)
"karma-jasmine" can be updated from ^0.2.2 to ^0.2.3 (Installed: 0.2.3, Latest: 0.2.3)
"karma-junit-reporter" can be updated from ^0.2.1 to ^0.2.2 (Installed: 0.2.2, Latest: 0.2.2)

Run 'npm-check-updates -u' to upgrade your package.json automatically

Finally, run the following command to update the plugins:-

npm-check-updates -u

Output:-

"karma-chrome-launcher" can be updated from ^0.1.4 to ^0.1.5 (Installed: 0.1.5, Latest: 0.1.5)
"karma-coverage" can be updated from ^0.2.4 to ^0.2.6 (Installed: 0.2.6, Latest: 0.2.6)
"karma-jasmine" can be updated from ^0.2.2 to ^0.2.3 (Installed: 0.2.3, Latest: 0.2.3)
"karma-junit-reporter" can be updated from ^0.2.1 to ^0.2.2 (Installed: 0.2.2, Latest: 0.2.2)

package.json upgraded

The plugin versions are successfully updated, and package.json is also updated accordingly.

{
  "name": "testKarma",
  "private": true,
  "devDependencies": {
    "karma": "^0.12.24",
    "karma-chrome-launcher": "^0.1.5",
    "karma-coverage": "^0.2.6",
    "karma-jasmine": "^0.2.3",
    "karma-junit-reporter": "^0.2.2",
    "karma-phantomjs-launcher": "^0.1.4"
  }
}

Why I Am Switching to Karma

OVERVIEW

JavaScript testing is hard. Most of the time, it is just plain difficult to set up the test harness just to run Javascript tests.

CURRENT STATE

Most of my team’s existing production web applications do not use any MV* frameworks. A few newer projects use Backbone/Marionette.

We rely on the following stack:-

We choose this direction because these Maven plugins provide good integration with Jenkins.

What is Wrong with the Current State

FUTURE STATE

We will now rely on the following stack:-

Why This is a Better State

  • Developed by Google developers, primary test harness for AngularJS.
  • Allows us to use Jasmine 2.x.
  • Test framework agnostic. Although we are using Jasmine now, we can easily switch to Mocha in the future.
  • MV* framework agnostic. It works with any MV* or homegrown framework.
  • Great integration with Jenkins.
  • Great integration with IntelliJ.
  • Ability to run tests on different browsers at the same time, such as Chrome, Firefox, PhantomJS, etc.

Jenkins: Getting Karma Generated Test Results to Appear in Maven Project Job

PROBLEM

Jenkins, for some reason, does not pick up Karma generated JUnit test reports even though they are created in the right directory… and apparently, it is a known problem. While Freestyle project job allows us to manually publish these JUnit reports, my intention is to rely on Maven project job to do the same thing.

PREREQUISITES

ENSURING KARMA GENERATED JUNIT REPORT SHOWS UP IN JENKINS…

Instead of manually running karma start command in Jenkins, we will rely on maven-karma-plugin to do this for us. The key here is to specify the correct <phase> so that Jenkins picks up and presents the generated report.

pom.xml

<build>
    <plugins>
        <plugin>
            <groupId>com.kelveden</groupId>
            <artifactId>maven-karma-plugin</artifactId>
            <version>1.6</version>
            <executions>
                <execution>
                    <phase>process-test-classes</phase>
                    <goals>
                        <goal>start</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <karmaExecutable>${basedir}/node_modules/karma/bin/karma</karmaExecutable>
                <configFile>src/test/resources/karma.jenkins.conf.js</configFile>
            </configuration>
        </plugin>
    </plugins>
</build>

CONFIGURING JENKINS

Since Karma test runner requires NodeJS, we will install NodeJS Plugin in Jenkins. This allows us to automatically install NodeJS from Jenkins.

Once installed, go to Manage Jenkins -> Configure System and go to NodeJS section:-

Although this section allows us to specify npm packages to install, I’m having trouble installing certain packages, such as karma-phantomjs-launcher. The phantomJS package invokes node install.js during the installation, however, the node command isn’t available in PATH environment variable at this point. Thus, the installation will always fail. So, the npm packages will be configured at the job level in the next step. Further, it is not a good idea to install Karma-related plugins globally here because every Jenkins job may use slightly different versions of the same plugin (think Spring or Hibernate versions in every job’s pom.xml).

Next, create a Maven project job and configure it.

Configuring Build Environment, Pre Steps and Build

We exposed NodeJS to PATH environment variable so that we can install phantomJS package.

Next, we created a pre-build step to execute npm install, which reads package.json from the job and installs the needed dependencies within the job.

Finally, we will want Maven to invoke test goal so that it runs both Java tests and Karma test runner.

Configuring Coverage Report

We provided the Karma generated coverage report XML file.

OUTCOME

When we run Build Now in Jenkins, both unit test and coverage reports will display both Java and JavaScript execution results.

Since we ran npm install as a pre-build step, the job will now have node_modules directory.