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

Review and improve React unit test coverage - Closes #531 #601

Merged
merged 20 commits into from
Aug 16, 2017
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ module.exports = function (config) {
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
browsers: ['Chrome'],
browsers: ['ChromeHeadless'],
singleRun: true,
browserNoActivityTimeout: 60000,
browserDisconnectTolerance: 3,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@
"imports-loader": "=0.6.5",
"js-nacl": "=1.2.2",
"json-loader": "=0.5.4",
"karma": "^1.7.0",
"karma": "=1.7.0",
"karma-chai": "=0.1.0",
"karma-chrome-launcher": "^2.2.0",
"karma-chrome-launcher": "=2.2.0",
"karma-coverage": "=1.1.1",
"karma-jenkins-reporter": "0.0.2",
"karma-mocha": "=1.3.0",
Expand Down
96 changes: 65 additions & 31 deletions src/components/account/accountComponent.test.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,106 @@
import React from 'react';
import chai, { expect } from 'chai';
import { spy } from 'sinon';
import sinon, { spy, mock } from 'sinon';
import sinonChai from 'sinon-chai';
import { shallow, mount } from 'enzyme';
import { Provider } from 'react-redux';
import * as accountApi from '../../utils/api/account';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You defined 'accountApi' and 'sinon' and 'mock' but you didn't use them. please remove them.

import store from '../../store';
import AccountComponent from './accountComponent';
import ClickToSend from '../send/clickToSend';

chai.use(sinonChai);

describe('AccountComponent', () => {
// Mocking store
const onActivePeerUpdated = () => {};
const peers = {
status: {
online: false,
},
data: {
currentPeer: 'localhost',
port: 4000,
options: {
name: 'Custom Node',
let props;

beforeEach(() => {
props = {
onActivePeerUpdated: sinon.spy(),
onAccountUpdated: sinon.spy(),
peers: {
status: {
online: false,
},
data: {
currentPeer: 'localhost',
port: 4000,
options: {
name: 'Custom Node',
},
},
},
account: {
isDelegate: false,
address: '16313739661670634666L',
username: 'lisk-nano',
balance: 1e8,
},
},
};
const testAccount = {
isDelegate: false,
address: '16313739661670634666L',
username: 'lisk-nano',
balance: 1e8,
};
};
});

it(' should render 3 article tags', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the extra space character before should.

const wrapper = shallow(<AccountComponent account={testAccount} peers={peers}
onActivePeerUpdated={onActivePeerUpdated} />);
const wrapper = shallow(<AccountComponent {...props} />);
expect(wrapper.find('article')).to.have.lengthOf(3);
});

it('depicts being online when peers.status.online is true', () => {
const onlinePeers = Object.assign({}, peers, { status: { online: true } });
const wrapper = shallow(<AccountComponent account={testAccount} peers={onlinePeers}
onActivePeerUpdated={onActivePeerUpdated} />);
props.peers.status.online = true;
const wrapper = shallow(<AccountComponent {...props} />);
const expectedValue = 'check';
expect(wrapper.find('.material-icons').text()).to.be.equal(expectedValue);
});

it('should render balance with ClickToSend component', () => {
const wrapper = mount(<Provider store={store}>
<AccountComponent account={testAccount} peers={peers}
onActivePeerUpdated={onActivePeerUpdated} />
<AccountComponent {...props} />
</Provider>);
expect(wrapper.find('.balance').find(ClickToSend)).to.have.lengthOf(1);
});

describe('componentDidMount', () => {
let accountApiMock;

beforeEach(() => {
accountApiMock = mock(accountApi);
});

afterEach(() => {
accountApiMock.restore();
});

it('should be called once', () => {
const actionSpy = spy(AccountComponent.prototype, 'componentDidMount');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to test 'componentDidMount' because it is part of React and it always runs.You should test functions that call inside the 'componentDidMount'.

mount(<Provider store={store}><AccountComponent account={testAccount} peers={peers}
onActivePeerUpdated={onActivePeerUpdated} /></Provider>);
mount(<Provider store={store}><AccountComponent {...props} /></Provider>);
expect(actionSpy).to.have.been.calledWith();
});

it('binds listener to beat event', () => {
const actionSpy = spy(document, 'addEventListener');
mount(<Provider store={store}><AccountComponent account={testAccount} peers={peers}
onActivePeerUpdated={onActivePeerUpdated} /></Provider>);
mount(<Provider store={store}><AccountComponent {...props} /></Provider>);
expect(actionSpy).to.have.been.calledWith();
});

it('calls props.onActivePeerUpdated', () => {
accountApiMock.expects('getAccountStatus').resolves({ success: true });
const wrapper = mount(<Provider store={store}><AccountComponent {...props} /></Provider>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You defined wrapper but you never used it. If it is extra, you need to remove it.

// TODO: this doesn't work for some reason
// expect(props.onActivePeerUpdated).to.have.been.calledWith();
});

it('calls props.onAccountUpdated', () => {
accountApiMock.expects('getAccount').resolves({ balance: props.account.balance });
const wrapper = mount(<Provider store={store}><AccountComponent {...props} /></Provider>);
// TODO: this doesn't work for some reason
// expect(props.onAccountUpdated).to.have.been.calledWith();
});

it('calls props.onTransactionsUpdated if getAccount returns different balance', () => {
accountApiMock.expects('transactions').resolves({ transactions: [{}] });
accountApiMock.expects('getAccount').resolves({ balance: props.account.balance + 1 });
const wrapper = mount(<Provider store={store}><AccountComponent {...props} /></Provider>);
// TODO: this doesn't work for some reason
// expect(props.onAccountUpdated).to.have.been.calledWith();
});
});
});
33 changes: 31 additions & 2 deletions src/components/account/index.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import sinon from 'sinon';
import * as accountActions from '../../actions/account';
import * as transactionsActions from '../../actions/transactions';
import * as peersActions from '../../actions/peers';
import Account from './index';
import AccountComponent from './accountComponent';

Expand Down Expand Up @@ -38,12 +42,37 @@ describe('Account', () => {
context: { store },
// childContextTypes: { store: PropTypes.object.isRequired },
};
let props;

it('should mount AccountComponent with appropriate properties', () => {
beforeEach(() => {
const mountedAccount = mount(<Account/>, options);
const props = mountedAccount.find(AccountComponent).props();
props = mountedAccount.find(AccountComponent).props();
});

it('should mount AccountComponent with appropriate properties', () => {
expect(props.peers).to.be.equal(peers);
expect(props.account).to.be.equal(account);
expect(typeof props.onActivePeerUpdated).to.be.equal('function');
});

it('should bind activePeerUpdate action to AccountComponent props.onActivePeerUpdated', () => {
const actionsSpy = sinon.spy(peersActions, 'activePeerUpdate');
props.onActivePeerUpdated({});
expect(actionsSpy).to.be.calledWith();
actionsSpy.restore();
});

it('should bind accountUpdated action to AccountComponent props.onAccountUpdated', () => {
const actionsSpy = sinon.spy(accountActions, 'accountUpdated');
props.onAccountUpdated({});
expect(actionsSpy).to.be.calledWith();
actionsSpy.restore();
});

it('should bind transactionsUpdated action to AccountComponent props.onTransactionsUpdated', () => {
const actionsSpy = sinon.spy(transactionsActions, 'transactionsUpdated');
props.onTransactionsUpdated({});
expect(actionsSpy).to.be.calledWith();
actionsSpy.restore();
});
});
53 changes: 42 additions & 11 deletions src/components/forging/forgingComponent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,42 @@ import React from 'react';
import chai, { expect } from 'chai';
import sinonChai from 'sinon-chai';
import { mount } from 'enzyme';
import sinon from 'sinon';
import ForgingComponent from './forgingComponent';
import * as forgingApi from '../../utils/api/forging';

chai.use(sinonChai);


describe('ForgingComponent', () => {
let wrapper;
const props = {
account: {
delegate: {},
isDelegate: true,
},
peers: {},
statistics: {},
forgedBlocks: [],
loadStats: () => {},
loadForgedBlocks: () => {},
};
let props;
let forginApiMock;

beforeEach(() => {
props = {
account: {
delegate: {},
isDelegate: true,
},
peers: {},
statistics: {},
forgedBlocks: [],
onForgingStatsUpdate: sinon.spy(),
onForgedBlocksLoaded: sinon.spy(),
};

forginApiMock = sinon.mock(forgingApi);
forginApiMock.expects('getForgedStats').atLeast(5).resolves({ success: true });
forginApiMock.expects('getForgedBlocks').resolves({ success: true, blocks: [] });

wrapper = mount(<ForgingComponent {...props} />);
});

afterEach(() => {
forginApiMock.restore();
});

it('should render ForgingTitle', () => {
expect(wrapper.find('ForgingTitle')).to.have.lengthOf(1);
});
Expand All @@ -40,4 +53,22 @@ describe('ForgingComponent', () => {
it('should render ForgedBlocks', () => {
expect(wrapper.find('ForgedBlocks')).to.have.lengthOf(1);
});

it('should render only a "not delegate" message if !props.account.isDelegate', () => {
props.account.isDelegate = false;
wrapper = mount(<ForgingComponent {...props} />);

expect(wrapper.find('ForgedBlocks')).to.have.lengthOf(0);
expect(wrapper.find('DelegateStats')).to.have.lengthOf(0);
expect(wrapper.find('p')).to.have.lengthOf(1);
});

// TODO: make these tests work
it.skip('should call props.onForgingStatsUpdate', () => {
expect(props.onForgingStatsUpdate).to.have.been.calledWith();
});

it.skip('should call props.onForgedBlocksLoaded', () => {
expect(props.onForgedBlocksLoaded).to.have.been.calledWith();
});
});
24 changes: 23 additions & 1 deletion src/components/forging/index.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import chai, { expect } from 'chai';
import sinonChai from 'sinon-chai';
import sinon from 'sinon';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import * as forgingActions from '../../actions/forging';
import Forging from './';
import store from '../../store';

chai.use(sinonChai);

Expand All @@ -13,10 +15,30 @@ describe('Forging', () => {
let wrapper;

beforeEach(() => {
const store = configureMockStore([])({
account: {},
peers: {},
forging: {
statistics: {},
forgedBlocks: [],
},
});
wrapper = mount(<Provider store={store}><Forging /></Provider>);
});

it('should render ForgingComponent', () => {
expect(wrapper.find('ForgingComponent')).to.have.lengthOf(1);
});

it('should bind updateForgedBlocks action to ForgingComponent props.onForgedBlocksLoaded', () => {
const actionsSpy = sinon.spy(forgingActions, 'updateForgedBlocks');
wrapper.find('ForgingComponent').props().onForgedBlocksLoaded([]);
expect(actionsSpy).to.be.calledWith();
});

it('should bind updateForgingStats action to ForgingComponent props.onForgingStatsUpdate', () => {
const actionsSpy = sinon.spy(forgingActions, 'updateForgingStats');
wrapper.find('ForgingComponent').props().onForgingStatsUpdate({});
expect(actionsSpy).to.be.calledWith();
});
});
38 changes: 38 additions & 0 deletions src/components/header/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import sinon from 'sinon';
import * as accountActions from '../../actions/account';
import * as dialogActions from '../../actions/dialog';
import Header from './index';
import store from '../../store';


describe('Header', () => {
let wrapper;

beforeEach(() => {
wrapper = mount(<Provider store={store}><Header /></Provider>);
});

it('should render HeaderElement', () => {
expect(wrapper.find('HeaderElement')).to.have.lengthOf(1);
});

it('should bind accountLoggedOut action to HeaderElement props.logOut', () => {
const actionsSpy = sinon.spy(accountActions, 'accountLoggedOut');
wrapper.find('HeaderElement').props().logOut({});
expect(actionsSpy).to.be.calledWith();
actionsSpy.restore();
});

it('should bind dialogDisplayed action to HeaderElement props.setActiveDialog', () => {
const actionsSpy = sinon.spy(dialogActions, 'dialogDisplayed');
wrapper.find('HeaderElement').props().setActiveDialog({});
expect(actionsSpy).to.be.calledWith();
actionsSpy.restore();
});
});


9 changes: 9 additions & 0 deletions src/components/login/loginForm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,13 @@ describe('LoginForm', () => {
expect(data).to.deep.equal(undefined);
});
});

describe('setActiveDialog', () => {
it('should return a dispatch object', () => {
const mountedAccount = mount(<Router><LoginForm/></Router>, options);
const props = mountedAccount.find(LoginFormComponent).props();
const data = props.setActiveDialog({});
expect(data).to.deep.equal(undefined);
});
});
});
Loading