Skip to content

Commit

Permalink
Merge pull request #39 from selvagsz/tests
Browse files Browse the repository at this point in the history
Tests
  • Loading branch information
selvagsz committed Jul 31, 2017
2 parents 3c8da8b + a7fe820 commit fe650ec
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 4,843 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# output
lib/
coverage/

# dependencies
/node_modules
Expand Down
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
sudo: false
language: node_js
node_js:
- "8"
- "6"
- "4"
- "5"
- stable
script:
- npm test
after_success: 'npm run coveralls'
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ React button component for handling async actions. Inspired from [ember-async-bu

[![npm version](https://badge.fury.io/js/react-async-button.svg)](https://www.npmjs.com/package/react-async-button)
[![Build Status](https://travis-ci.org/selvagsz/react-async-button.svg?branch=master)](https://travis-ci.org/selvagsz/react-async-button)
[![Coverage Status](https://coveralls.io/repos/github/selvagsz/react-async-button/badge.svg?branch=master)](https://coveralls.io/github/selvagsz/react-async-button)

## Installation

Expand Down
22 changes: 17 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"build": "npm run clean && npm run build:commonjs && npm run example:build",
"test": "jest",
"prepublish": "npm test && npm run build",
"precommit": "lint-staged"
"precommit": "lint-staged",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
},
"lint-staged": {
"src/**/*.js": [
Expand Down Expand Up @@ -39,33 +40,44 @@
"devDependencies": {
"babel-cli": "^6.10.1",
"babel-core": "^6.9.1",
"babel-jest": "^13.0.0",
"babel-jest": "^20.0.3",
"babel-loader": "^6.2.5",
"babel-polyfill": "^6.9.1",
"babel-preset-es2015": "^6.9.0",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.16.0",
"babel-preset-stage-1": "^6.5.0",
"coveralls": "^2.13.1",
"css-loader": "^0.25.0",
"enzyme": "^2.3.0",
"husky": "^0.14.1",
"jest-cli": "^13.1.0",
"jest": "^20.0.4",
"jest-cli": "^20.0.4",
"lint-staged": "^4.0.0",
"prettier": "^1.4.4",
"react": "^15.1.0",
"react-addons-test-utils": "^15.1.0",
"react-dom": "^15.1.0",
"react-fa": "^4.1.2",
"react-highlight": "^0.9.0",
"react-test-renderer": "^15.6.1",
"sinon": "^1.17.4",
"style-loader": "^0.13.1",
"webpack": "^1.13.2",
"webpack-dev-server": "^1.16.2"
},
"dependencies": {},
"dependencies": {
"prop-types": "^15.5.10"
},
"jest": {
"automock": false,
"testPathDirs": [
"verbose": true,
"collectCoverage": true,
"coverageReporters": [
"html",
"text"
],
"roots": [
"src"
],
"unmockedModulePathPatterns": [
Expand Down
35 changes: 35 additions & 0 deletions src/__tests__/utils-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* global describe, it, expect */
import { classNames } from '../utils';

describe('Utils - classNames', () => {
it('Should return empty string when falsy class names are passed', () => {
let emptyKlassName;
let falsyKlassName = false;
let falsyKlassObject = {
loading: false,
pending: false,
rejected: false,
};

expect(classNames(emptyKlassName)).toBe('');
expect(classNames(falsyKlassName)).toBe('');
expect(classNames(falsyKlassObject)).toBe('');
expect(classNames(emptyKlassName, falsyKlassName, falsyKlassObject)).toBe(
''
);
});

it('Should return comma separated classes when valid classNames are passed', () => {
let klassName = 'AsyncButton';
let klassObject = {
loading: false,
pending: true,
rejected: true,
};

expect(classNames(klassName)).toBe('AsyncButton');
expect(classNames(klassName, klassObject)).toBe(
'AsyncButton pending rejected'
);
});
});
10 changes: 8 additions & 2 deletions src/components/AsyncButton.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { PropTypes } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { classNames } from '../utils';

export default class AsyncButton extends React.Component {
state = {
Expand Down Expand Up @@ -82,7 +84,11 @@ export default class AsyncButton extends React.Component {
return (
<button
{...attributes}
className={`${className} ${isPending ? loadingClass : ''} ${isFulfilled ? fulFilledClass : ''} ${isRejected ? rejectedClass : ''}`}
className={classNames(className, {
[loadingClass]: isPending,
[fulFilledClass]: isFulfilled,
[rejectedClass]: isRejected,
})}
disabled={isDisabled}
onClick={event => this.handleClick(event)}
>
Expand Down
155 changes: 147 additions & 8 deletions src/components/__tests__/AsyncButton-test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/* global describe, it, expect */
// /* global describe, it, expect */
import React from 'react';
import AsyncButton from '../AsyncButton.js';
import AsyncButton from '../AsyncButton';
import { shallow, mount } from 'enzyme';
import sinon from 'sinon';

describe('main.js', () => {
it('Should render `button` tag', () => {
describe('<AsyncButton />', () => {
it('should render `button` tag', () => {
const wrapper = shallow(<AsyncButton />);
expect(wrapper.find('button').length).toBe(1);
});

it('Component should delegate classNames & disabled attr to button', () => {
it('should delegate classNames & disabled attr to button', () => {
const wrapper = shallow(
<AsyncButton className="btn btn-default" disabled text="Save" />
);
Expand All @@ -21,7 +21,7 @@ describe('main.js', () => {
expect($button.text()).toBe('Save');
});

it('Should trigger click handler', () => {
it('should trigger click handler', () => {
const clickHandler = sinon.spy();
const wrapper = shallow(
<AsyncButton
Expand All @@ -42,14 +42,153 @@ describe('main.js', () => {
resolve = _resolve;
});
let onClick = () => promise;
const wrapper = mount(<AsyncButton onClick={onClick} />);
const Component = <AsyncButton onClick={onClick} />;
const wrapper = mount(Component);
wrapper.find('button').simulate('click');
wrapper.unmount();
wrapper.node.setState = sinon.spy();

resolve();
return Promise.resolve().then(() => {
expect(wrapper.node.setState.callCount).toBe(0);
});
});

it('should not call setState after unmount on reject', () => {
let reject;
const promise = new Promise((_resolve, _reject) => {
reject = _reject;
});
let onClick = () => promise;
const Component = <AsyncButton onClick={onClick} />;
const wrapper = mount(Component);
wrapper.find('button').simulate('click');
wrapper.unmount();
wrapper.node.setState = sinon.spy();
reject();
return Promise.resolve().then(() => {
promise.catch(() => {
expect(wrapper.node.setState.callCount).toBe(0);
});
});
});

it('should set the fulFilledClass & fulFilledText on promise resolve', () => {
let resolve;
const promise = new Promise(_resolve => {
resolve = _resolve;
});
let onClick = () => promise;

const wrapper = mount(
<AsyncButton
text="Save"
pendingText="Saving..."
loadingClass="loading"
fulFilledClass="success"
fulFilledText="Saved!"
onClick={onClick}
/>
);
const $button = wrapper.find('button');
expect($button.text()).toBe('Save');
expect($button.hasClass('loading')).toBe(false);
expect($button.prop('disabled')).toBe(false);

$button.simulate('click');
expect($button.text()).toBe('Saving...');
expect($button.hasClass('loading')).toBe(true);
expect($button.prop('disabled')).toBe(true);

resolve();
return Promise.resolve().then(() => {
expect($button.text()).toBe('Saved!');
expect($button.hasClass('loading')).toBe(false);
expect($button.hasClass('success')).toBe(true);
expect($button.prop('disabled')).toBe(false);
});
});

it('should set the rejectedClass & rejectedText on promise reject', () => {
let reject;
const promise = new Promise((_resolve, _reject) => {
reject = _reject;
});
let onClick = () => promise;

const wrapper = mount(
<AsyncButton
text="Save"
pendingText="Saving..."
loadingClass="loading"
fulFilledClass="success"
fulFilledText="Saved!"
rejectedClass="error"
rejectedText="Try Again!"
onClick={onClick}
/>
);
const $button = wrapper.find('button');
expect($button.text()).toBe('Save');
expect($button.hasClass('loading')).toBe(false);
expect($button.prop('disabled')).toBe(false);

$button.simulate('click');
expect($button.text()).toBe('Saving...');
expect($button.hasClass('loading')).toBe(true);
expect($button.prop('disabled')).toBe(true);

reject();
return Promise.resolve().then(() => {
promise.catch(error => {
expect($button.text()).toBe('Try Again!');
expect($button.hasClass('loading')).toBe(false);
expect($button.hasClass('success')).toBe(false);
expect($button.hasClass('error')).toBe(true);
expect($button.prop('disabled')).toBe(false);
});
});
});
});

describe('AsyncButton :- Block form', () => {
it('should set the fulFilledClass & fulFilledText on promise resolve', () => {
let resolve;
const promise = new Promise(_resolve => {
resolve = _resolve;
});
let onClick = () => promise;

const wrapper = mount(
<AsyncButton
text="Save"
pendingText="Saving..."
loadingClass="loading"
fulFilledClass="success"
fulFilledText="Saved!"
onClick={onClick}
>
{({ buttonText, isPending, isFulfilled, isRejected }) =>
<span>
{buttonText}
</span>}
</AsyncButton>
);
const $button = wrapper.find('button');
expect($button.text()).toBe('Save');
expect($button.hasClass('loading')).toBe(false);
expect($button.prop('disabled')).toBe(false);

$button.simulate('click');
expect($button.text()).toBe('Saving...');
expect($button.hasClass('loading')).toBe(true);
expect($button.prop('disabled')).toBe(true);

resolve();
return Promise.resolve().then(() => {
expect($button.text()).toBe('Saved!');
expect($button.hasClass('loading')).toBe(false);
expect($button.hasClass('success')).toBe(true);
expect($button.prop('disabled')).toBe(false);
});
});
});
36 changes: 36 additions & 0 deletions src/components/__tests__/AsyncButton.snapshot-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import AsyncButton from '../AsyncButton';
import renderer from 'react-test-renderer';

const expectToMatchSnapshot = component => {
expect(renderer.create(component).toJSON()).toMatchSnapshot();
};

describe('<AsyncButton /> Snapshots', () => {
it('should have sane defaults', () => {
expectToMatchSnapshot(<AsyncButton />);
});

it('should delegate text, className & disabled props', () => {
expectToMatchSnapshot(
<AsyncButton className="btn btn-default" text="Save" disabled={true} />
);
});
});

describe('Block form :- <AsyncButton></AsyncButton>', () => {
it('should have sane defaults', () => {
expectToMatchSnapshot(<AsyncButton>Save</AsyncButton>);
});

it('should delegate text, className & disabled props', () => {
expectToMatchSnapshot(
<AsyncButton className="btn btn-default" text="Save" disabled={true}>
{({ buttonText, isPending }) =>
<span>
{buttonText}
</span>}
</AsyncButton>
);
});
});
Loading

0 comments on commit fe650ec

Please sign in to comment.