Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Merge pull request #954 from LiskHQ/703-unit-tests
Browse files Browse the repository at this point in the history
Remove direct calls to methods of components in unit tests - Closes #703
  • Loading branch information
reyraa authored Nov 8, 2017
2 parents 8a2b116 + 90a121a commit 025c683
Show file tree
Hide file tree
Showing 14 changed files with 299 additions and 245 deletions.
5 changes: 4 additions & 1 deletion src/components/dialog/dialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ describe('Dialog', () => {
});

it('should fix the route if there are two dialog names', () => {
wrapper.instance().componentDidUpdate();
const newProps = Object.assign({}, { dialog: dialogProps, history }, props);
newProps.dialog.title = 'Send1';
// trying to update the component
wrapper.setProps(newProps);
expect(history.push).to.have.been.calledWith();
});
});
4 changes: 2 additions & 2 deletions src/components/formattedNumber/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import * as locales from 'numeral/locales'; // eslint-disable-line
import { translate } from 'react-i18next';
import i18n from '../../i18n';

const FormattedNumber = (props) => {
const FormattedNumber = ({ val }) => {
// set numeral language
numeral.locale(i18n.language);
const formatedNumber = numeral(props.val).format('0,0.[0000000000000]');
const formatedNumber = numeral(val).format('0,0.[0000000000000]');
return <span>{formatedNumber}</span>;
};

Expand Down
52 changes: 52 additions & 0 deletions src/components/formattedNumber/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import PropTypes from 'prop-types';
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import FormattedNumber from './index';
import i18n from '../../i18n';


describe('FormattedNumber', () => {
const options = {
context: { i18n },
childContextTypes: {
i18n: PropTypes.object.isRequired,
},
};

it('renders 0 if raw value is 0', () => {
const value = {
raw: 0,
formatted: '0',
};
const wrapper = mount(<FormattedNumber val={value.raw} />, options);
expect(wrapper.find('span').text()).to.equal(value.formatted);
});

it('renders 1,000 if raw value is 1000', () => {
const value = {
raw: 1234,
formatted: '1,234',
};
const wrapper = mount(<FormattedNumber val={value.raw} />, options);
expect(wrapper.find('span').text()).to.equal(value.formatted);
});

it('renders 1,000.95 if raw value is 1000', () => {
const value = {
raw: 1234.56,
formatted: '1,234.56',
};
const wrapper = mount(<FormattedNumber val={value.raw} />, options);
expect(wrapper.find('span').text()).to.equal(value.formatted);
});

it('renders 10.01 if raw value is 10.01', () => {
const value = {
raw: 123.45,
formatted: '123.45',
};
const wrapper = mount(<FormattedNumber val={value.raw} />, options);
expect(wrapper.find('span').text()).to.equal(value.formatted);
});
});
2 changes: 1 addition & 1 deletion src/components/header/header.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('Header', () => {
expect(wrapper.find(RelativeLink)).to.have.length(9);
});

it('should have an image with srouce of "logo"', () => {
it('should have an image with source of "logo"', () => {
expect(wrapper.contains(<img className={styles.logo} src={logo} alt="logo" />))
.to.be.equal(true);
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/loadingBar/loadingBar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('LoadingBar Container', () => {
expect(wrapper.find('ProgressBar')).to.have.lengthOf(1);
});

it('should not show ProgresBar if props.loading.length is 0', () => {
it('should not show ProgressBar if props.loading.length is 0', () => {
const wrapper = mount(<LoadingBar loading={[]} />);
expect(wrapper.find('ProgressBar')).to.have.lengthOf(0);
});
Expand Down
37 changes: 18 additions & 19 deletions src/components/login/login.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import Login from './login';

describe('Login', () => {
let wrapper;
const address = 'http:localhost:8080';
const passphrase = 'recipe bomb asset salon coil symbol tiger engine assist pact pumpkin';
// Mocking store
const account = {
isDelegate: false,
Expand Down Expand Up @@ -58,16 +60,14 @@ describe('Login', () => {
});

it('should show error about passphrase length if passphrase is have wrong length', () => {
const passphrase = 'recipe bomb asset salon coil symbol tiger engine assist pact pumpkin';
const expectedError = 'Passphrase should have 12 words, entered passphrase has 11';
wrapper.find('.passphrase input').simulate('change', { target: { value: ' ' } });
wrapper.find('.passphrase input').simulate('change', { target: { value: passphrase } });
expect(wrapper.find('.passphrase')).to.contain(expectedError);
});
});

describe('componentDidUpdate', () => {
const address = 'http:localhost:8080';
describe('History management', () => {
props.account = { address: 'dummy' };

it('calls this.props.history.replace(\'/main/transactions\')', () => {
Expand Down Expand Up @@ -96,8 +96,13 @@ describe('Login', () => {

it('calls localStorage.setItem(\'address\', address) if this.state.address', () => {
const spyFn = spy(localStorage, 'setItem');
wrapper = shallow(<Login {...props}/>, options);
wrapper.setState({ address });
wrapper = mount(<Router><Login {...props}/></Router>, options);
// set the network dropdown
wrapper.find('div.network').simulate('click');
// select custom node
wrapper.find('div.network ul li').at(2).simulate('click');
// fill the address
wrapper.find('Input.address input').simulate('change', { target: { value: address } });
wrapper.setProps(props);
expect(spyFn).to.have.been.calledWith('address', address);

Expand All @@ -106,23 +111,17 @@ describe('Login', () => {
});
});

describe('changeHandler', () => {
it('call setState with matching data', () => {
wrapper = shallow(<Login {...props}/>, options);
const key = 'network';
const value = 0;
const spyFn = spy(Login.prototype, 'setState');
wrapper.instance().changeHandler(key, value);
expect(spyFn).to.have.been.calledWith({ [key]: value });
});
});

describe('onLoginSubmission', () => {
describe('After submission', () => {
it('it should call activePeerSet', () => {
const spyActivePeerSet = spy(props, 'activePeerSet');

wrapper = shallow(<Login {...props}/>, options);
wrapper.instance().onLoginSubmission();
wrapper = mount(<Router><Login {...props}/></Router>, options);
// Filling the login form
wrapper.find('div.network').simulate('click');
wrapper.find('div.network ul li').at(2).simulate('click');
wrapper.find('Input.address input').simulate('change', { target: { value: address } });
wrapper.find('Input.passphrase input').simulate('change', { target: { value: passphrase } });
wrapper.find('form').simulate('submit');
expect(spyActivePeerSet).to.have.been.calledWith();
});
});
Expand Down
5 changes: 3 additions & 2 deletions src/components/passphrase/passphraseGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ class PassphraseGenerator extends React.Component {

/**
* Tests useragent with a regexp and defines if the account is mobile device
* it is on class so that we can mock it in unit tests
*
* @param {String} [agent] - The useragent string, This parameter is used for
* unit testing purpose
* @returns {Boolean} - whether the agent represents a mobile device or not
*/
// it is on class so that we can mock it in unit tests
// eslint-disable-next-line class-methods-use-this
isTouchDevice(agent) {
return (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(agent || navigator.userAgent || navigator.vendor || window.opera));
Expand Down Expand Up @@ -88,10 +88,11 @@ class PassphraseGenerator extends React.Component {
}

render() {
const isTouch = this.isTouchDevice(this.props.agent);
return (
<div className={`${grid.row} ${grid['center-xs']}`} >
<div className={grid['col-xs-12']}>
{this.isTouchDevice() ?
{isTouch ?
<div>
<p>Enter text below to generate random bytes</p>
<Input onChange={this.seedGeneratorBoundToThis}
Expand Down
82 changes: 19 additions & 63 deletions src/components/passphrase/passphraseGenerator.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { expect } from 'chai';
import { spy, mock } from 'sinon';
import { mount, shallow } from 'enzyme';
import { spy } from 'sinon';
import { mount } from 'enzyme';
import PassphraseGenerator from './passphraseGenerator';
import * as passphraseUtil from '../../utils/passphrase';


describe('PassphraseGenerator', () => {
Expand All @@ -11,88 +12,43 @@ describe('PassphraseGenerator', () => {
changeHandler: () => {},
t: key => key,
};
const mockEvent = {
pageX: 140,
pageY: 140,
};

it('calls setState to setValues locally', () => {
const wrapper = shallow(<PassphraseGenerator {...props}/>);
const spyFn = spy(wrapper.instance(), 'setState');
wrapper.instance().seedGenerator(mockEvent);
expect(spyFn).to.have.been.calledWith();
wrapper.instance().setState.restore();
});

it('shows an Input fallback if this.isTouchDevice()', () => {
const wrapper = mount(<PassphraseGenerator {...props}/>);
const isTouchDeviceMock = mock(wrapper.instance()).expects('isTouchDevice');
isTouchDeviceMock.returns(true);
wrapper.instance().setState({}); // to rerender the component
wrapper.update();
const wrapper = mount(<PassphraseGenerator {...props} agent='ipad'/>);
expect(wrapper.find('Input.touch-fallback textarea')).to.have.lengthOf(1);
});

it('shows at least some progress on pressing input if this.isTouchDevice()', () => {
const wrapper = mount(<PassphraseGenerator {...props}/>);
const isTouchDeviceMock = mock(wrapper.instance()).expects('isTouchDevice');
isTouchDeviceMock.returns(true).twice();
wrapper.instance().setState({}); // to rerender the component
wrapper.update();
const wrapper = mount(<PassphraseGenerator {...props} agent='ipad'/>);
wrapper.find('Input.touch-fallback textarea').simulate('change', { target: { value: 'random key presses' } });
expect(wrapper.find('ProgressBar').props().value).to.be.at.least(1);
});

it('removes mousemove event listener in componentWillUnmount', () => {
const wrapper = mount(<PassphraseGenerator {...props}/>);
const documentSpy = spy(document, 'removeEventListener');
wrapper.instance().componentWillUnmount();
wrapper.unmount();
expect(documentSpy).to.have.be.been.calledWith('mousemove');
documentSpy.restore();
});

it('sets "data" and "lastCaptured" if distance is over 120', () => {
const wrapper = shallow(<PassphraseGenerator {...props}/>);
wrapper.instance().seedGenerator(mockEvent);

expect(wrapper.instance().state.data).to.not.equal(undefined);
expect(wrapper.instance().state.lastCaptured).to.deep.equal({
x: 140,
y: 140,
});
});

it('should do nothing if distance is bellow 120', () => {
const wrapper = shallow(<PassphraseGenerator {...props}/>);
const nativeEvent = {
pageX: 10,
pageY: 10,
};
wrapper.instance().seedGenerator(nativeEvent);
it('should call generateSeed for every event triggered', () => {
const generateSeedSpy = spy(passphraseUtil, 'generateSeed');
const wrapper = mount(<PassphraseGenerator {...props} agent='ipad'/>);
wrapper.find('Input.touch-fallback textarea').simulate('change', { target: { value: 'random key presses' } });

expect(wrapper.instance().state.data).to.be.equal(undefined);
expect(wrapper.instance().state.lastCaptured).to.deep.equal({
x: 0,
y: 0,
});
expect(generateSeedSpy.callCount).to.be.equal(1);
});

it('should generate passphrase if seed is completed', () => {
const wrapper = shallow(<PassphraseGenerator {...props}/>);
// set mock data
wrapper.instance().setState({
data: {
seed: ['e6', '3c', 'd1', '36', 'e9', '70', '5f',
'c0', '4d', '31', 'ef', 'b8', 'd6', '53', '48', '11'],
percentage: 100,
step: 1,
byte: [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0],
},
});

wrapper.instance().seedGenerator(mockEvent);
it('should call generatePassphrase after enough event passed', () => {
const generatePassphraseSpy = spy(passphraseUtil, 'generatePassphrase');
const wrapper = mount(<PassphraseGenerator {...props} agent='ipad'/>);
const input = wrapper.find('Input.touch-fallback textarea');
for (let i = 0; i < 101; i++) {
input.simulate('change', { target: { value: 'random key presses' } });
}

expect(wrapper.instance().state.passphrase).to.not.equal(undefined);
expect(generatePassphraseSpy.callCount).to.be.equal(1);
});
});
});
5 changes: 3 additions & 2 deletions src/components/passphrase/passphraseVerifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ class PassphraseConfirmator extends React.Component {

componentDidMount() {
this.props.updateAnswer(false);
this.hideRandomWord.call(this);
// this.props.randomIndex is used in unit teasing
this.hideRandomWord.call(this, this.props.randomIndex);
}

hideRandomWord(rand = Math.random()) {
Expand All @@ -40,7 +41,7 @@ class PassphraseConfirmator extends React.Component {
return (
<div className={`passphrase-verifier ${grid.row} ${grid['start-xs']}`}>
<div className={grid['col-xs-12']}>
<p>
<p className='passphrase-holder'>
<span>{this.state.passphraseParts[0]}</span>
<span className={styles.missing}>-----</span>
<span>{this.state.passphraseParts[1]}</span>
Expand Down
Loading

0 comments on commit 025c683

Please sign in to comment.