Skip to content

Commit

Permalink
Added component test and test boilerplate. Fixes #1
Browse files Browse the repository at this point in the history
  • Loading branch information
raulrene committed Oct 12, 2017
1 parent 23bd26c commit 2d1d7e2
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 2 deletions.
13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"license": "MIT",
"main": "dist/index.js",
"scripts": {
"build": "webpack -d"
"build": "webpack -d",
"test": "webpack --config webpack.spec.config.js --hide-modules; node_modules/.bin/mocha --require jsdom-global/register dist/index.spec.js && rm dist/index.spec.js"
},
"keywords": [
"react-utils-button",
Expand All @@ -30,8 +31,18 @@
"babel-preset-es2017": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"chai": "^4.1.2",
"chai-enzyme": "^0.8.0",
"clean-webpack-plugin": "^0.1.17",
"enzyme": "2.8.0",
"jsdom": "^11.3.0",
"jsdom-global": "^3.0.2",
"mocha": "^4.0.1",
"react": "^15.6.1",
"react-addons-test-utils": "^15.4.0",
"react-dom": "^15.0.0",
"sinon": "^4.0.1",
"sinon-chai": "^2.14.0",
"webpack": "^2.4.1"
}
}
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ReactUtilsButton extends React.Component {
onClick={!disabled ? onClick : () => {}} // Only trigger onClick if component is not disabled
disabled={disabled ? 'disabled' : undefined}>

<span className='input-button-value'>{value}</span>
<span className='utils-button-value'>{value}</span>
</button>
);
}
Expand Down
77 changes: 77 additions & 0 deletions src/index.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import chai, { expect } from 'chai';
import sinonChai from 'sinon-chai';
import chaiEnzyme from 'chai-enzyme';
import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import Button from './index';

chai.use(sinonChai);
chai.use(chaiEnzyme());

describe('Button Component', () => {
describe('when rendering the component', () => {
interceptConsoleErrors();
const sandbox = sinon.sandbox.create();
afterEach(() => sandbox.restore());

it('should throw warning for missing prop "value"', () => {
shallow(<Button />);
expectMissingProp('value', 'ReactUtilsButton');
});

it('should always have class "utils-button"', () => {
const wrapper = shallow(<Button value='click'/>);
expect(wrapper).to.have.className('utils-button');
});

it('should always have custom class if provided in props', () => {
const wrapper = shallow(<Button className='custom-class' value='click'/>);
expect(wrapper).to.have.className('custom-class');
});

it('should always have "utils-button--active" if active props is received', () => {
const wrapper = shallow(<Button active={true} value='click'/>);
expect(wrapper).to.have.className('utils-button--active');
});

it('should trigger the onClick callback when button is pressed', () => {
const callback = sandbox.stub();
const wrapper = shallow(<Button onClick={callback} value='click'/>);
wrapper.simulate('click');
expect(callback).to.have.been.calledOnce;
});

it('should not trigger onClick callback when it\'s disabled', () => {
const callback = sandbox.stub();
const wrapper = shallow(<Button onClick={callback} disabled={true} value='click'/>);
wrapper.simulate('click');
expect(callback).not.to.have.been.called;
});

it('should be disabled when prop is received', () => {
const wrapper = shallow(<Button disabled={true} value='click'/>);
expect(wrapper).to.have.attr('disabled', 'disabled');
});

it('should have the value received in props', () => {
const wrapper = shallow(<Button value='click'/>);
expect(wrapper.find('.utils-button-value')).to.have.text('click');
});
});
});

/** Intercept console errors (and warnings) in unit tests in order to check what errors ere printed */
function interceptConsoleErrors() {
beforeEach(() => sinon.stub(console, 'error'));
afterEach(() => console.error.restore());
}

/**
* Assert for a missing property inside a component
* @param prop {String} Property name
* @param component {String} Component name
*/
function expectMissingProp(prop, component) {
sinon.assert.calledWithMatch(console.error, new RegExp(`Failed prop type: The prop \`${prop}\` is marked as required in \`${component}\``));
}
34 changes: 34 additions & 0 deletions webpack.spec.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

const path = require('path');

const config = {
entry: path.join(__dirname, 'src', 'index.spec.js'),
output: {
path: path.join(__dirname, 'dist'),
filename: 'index.spec.js'
},

// Some libs need to be set as externals for enzyme
externals: {
'react/addons': 'react',
'react/lib/ExecutionEnvironment': 'react',
'react/lib/ReactContext': 'react'
},

module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},

resolve: {
modules: ['node_modules']
}
};

module.exports = config;

0 comments on commit 2d1d7e2

Please sign in to comment.