Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase test coverage of ignored files above 80% Part 4 - Closes #536 #547

Merged
merged 18 commits into from
Mar 20, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 1 addition & 10 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module.exports = function (config) {
'src/components/menuBar/menuBar.js',
'src/components/accountVisual/demo.js',
'src/components/delegateList/votingHeader.js',
'src/components/dialog/index.js',
'src/components/app/index.js',
'src/components/transactions/transactionList.js',
'src/components/relativeLink/index.js',
Expand All @@ -86,16 +87,6 @@ module.exports = function (config) {
'src/components/register/index.js',
'src/components/register/register.js',
'src/components/transactions/transactionOverview.js',
'src/components/transactions/transactionDetailView.js',
'src/components/voting/index.js',
'src/components/delegateList/index.js',
'src/components/delegateList/delegateList.js',
'src/components/dialog/index.js',
'src/components/dialog/dialog.js',
'src/components/toaster/index.js',
'src/components/mainMenu/mainMenu.js',
'src/components/setting/setting.js',
'src/components/votesPreview/index.js',
],
overrides: {
'src/store/**/*.js': {
Expand Down
151 changes: 131 additions & 20 deletions src/components/delegateList/delegateList.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import PropTypes from 'prop-types';
import { BrowserRouter as Router } from 'react-router-dom';
import sinon from 'sinon';
import DelegateList from './delegateList';
import { mountWithContext } from './../../../test/utils/mountHelpers';
import store from '../../store';
import history from '../../history';
import i18n from '../../i18n';
import voteFilters from './../../constants/voteFilters';

describe('DelegateList', () => {
let wrapper;
Expand Down Expand Up @@ -43,21 +40,21 @@ describe('DelegateList', () => {
delegatesFetched: sinon.spy(),
t: key => key,
};

let clock;
let loadMoreSpy;

beforeEach(() => {
wrapper = mount(<Router><DelegateList {...props}></DelegateList></Router>,
{
context: { store, history, i18n },
childContextTypes: {
store: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
i18n: PropTypes.object.isRequired,
},
},
);
clock = sinon.useFakeTimers({
toFake: ['setTimeout', 'clearTimeout', 'Date'],
});
loadMoreSpy = sinon.spy(DelegateList.prototype, 'loadMore');
wrapper = mountWithContext(<DelegateList {...props}/>, {});
});

afterEach(() => {
// Voting.prototype.setStatus.restore();
clock.restore();
loadMoreSpy.restore();
});

it('should render VotingHeader', () => {
Expand All @@ -73,11 +70,9 @@ describe('DelegateList', () => {
});

it('should define search method to reload delegates based on given query', () => {
const clock = sinon.useFakeTimers({
toFake: ['setTimeout', 'clearTimeout', 'Date'],
});
props.delegatesFetched.reset();
wrapper.find('.search input').at(0).simulate('change', { nativeEvent: { target: { value: 'query' } } });
wrapper.find('.search input')
.at(0).simulate('change', { nativeEvent: { target: { value: 'query' } } });
clock.tick(251);
expect(props.delegatesFetched).to.be.calledWith({
activePeer: props.activePeer,
Expand All @@ -87,4 +82,120 @@ describe('DelegateList', () => {
});
clock.restore();
});

it('should call loadMore and not loadDelegates if still loading', () => {
const loadMoreProps = {
...props,
totalDelegates: 100,
};
wrapper = mountWithContext(<DelegateList {...loadMoreProps}/>, {});
const waypoint = wrapper.find('Waypoint').at(1);
waypoint.props().onEnter();
expect(loadMoreSpy).to.have.been.calledWith();
expect(wrapper).to.have.exactly(delegates.length).descendants('.delegate-row');
});

it('should call loadMore and loadDelegates if not still loading', () => {
const loadMoreProps = {
...props,
totalDelegates: 100,
};
const loadDelegates = sinon.spy(DelegateList.prototype, 'loadDelegates');
wrapper = mountWithContext(<DelegateList {...props}/>, { ...store });
const waypoint = wrapper.find('Waypoint').at(1);

const nextProps = {
delegates: [
...loadMoreProps.delegates,
{
address: 'address 3',
username: 'username3',
publicKey: 'sample_key',
rank: 23,
}],
};

/*
Force trigger componentWillUpdate,
will reset freezeLoading flag to cover loadMore
*/
wrapper.setProps(nextProps);
clock.tick(300);
waypoint.props().onEnter();
expect(loadMoreSpy).to.have.been.calledWith();
expect(wrapper).to.have.exactly(delegates.length + 1).descendants('.delegate-row');
loadDelegates.restore();
clock.restore();
});

it('should filter voted delegates', () => {
const filterVotedProps = {
...props,
};
wrapper = mountWithContext(<DelegateList {...filterVotedProps}/>, {});
wrapper.find('.transaction-filter-item').at(voteFilters.voted).simulate('click');
wrapper.update();
const delegateRow = wrapper.find('.delegate-row');
expect(delegateRow.length).to.equal(delegates.length - 1);
expect(delegateRow.find('li').at(2)).to.have.text(delegates[0].username);
});

it('should filter notVoted delegates', () => {
const filterVotedProps = {
...props,
};
wrapper = mountWithContext(<DelegateList {...filterVotedProps}/>, {});
wrapper.find('.transaction-filter-item').at(voteFilters.notVoted).simulate('click');
wrapper.update();
const delegateRow = wrapper.find('.delegate-row');
expect(delegateRow.length).to.equal(delegates.length - 1);
expect(delegateRow.find('li').at(2)).to.have.text(delegates[1].username);
});

it('should show no voted message', () => {
const emptyMessageProps = {
...props,
};
emptyMessageProps.delegates = [];
emptyMessageProps.votes = {};
wrapper = mountWithContext(<DelegateList {...emptyMessageProps}/>, {});
const nextProps = {
delegates: [],
};

/*
force to have no delegates and
trigger voted filter
*/
wrapper.setProps(nextProps);
clock.tick(300);
wrapper.find('.transaction-filter-item').at(voteFilters.voted).simulate('click');
wrapper.update();
const delegateRow = wrapper.find('.empty-message');
expect(delegateRow).to.have.text('You have not voted yet.');
});

it('should show no results message', () => {
const emptyMessageProps = {
...props,
};
emptyMessageProps.delegates = [];
emptyMessageProps.votes = {};
wrapper = mountWithContext(<DelegateList {...emptyMessageProps}/>, {});
const nextProps = {
delegates: [delegates[1]],
};

/*
force to reset isInitial flag
and then reset delegates
*/
wrapper.setProps(nextProps);
clock.tick(300);
wrapper.update();

wrapper.setProps({ delegates: [] });
const delegateRow = wrapper.find('.empty-message');
expect(delegateRow).to.have.text('No delegates found.');
});
});
56 changes: 56 additions & 0 deletions src/components/delegateList/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import PropTypes from 'prop-types';
import { BrowserRouter as Router } from 'react-router-dom';
import store from '../../store';
import DelegateList from './index';
import i18n from '../../i18n';
import history from '../../history';

describe('DelegateListHOC', () => {
let wrapper;
const account = { address: '16313739661670634666L' };
const peers = { data: {} };
const voting = {
votes: {},
delegates: [],
totalDelegates: 10,
refresh: false,
};

beforeEach(() => {
store.getState = () => ({
peers,
account,
voting,
loading: [],
});
wrapper = mount(<Provider store={store}><Router><DelegateList /></Router></Provider>, {
context: { store, history, i18n },
childContextTypes: {
store: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
i18n: PropTypes.object.isRequired,
},
});
});

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

it('should mount DelegatesList with appropriate properties', () => {
const props = wrapper.find('DelegateList').first().props();
expect(props.refreshDelegates).to.be.equal(voting.refresh);
expect(props.delegates).to.be.equal(voting.delegates);
expect(props.votes).to.be.equal(voting.votes);
expect(props.totalDelegates).to.be.equal(voting.totalDelegates);
expect(props.activePeer).to.be.equal(peers.data);
expect(props.address).to.be.equal(account.address);
expect(typeof props.voteToggled).to.be.equal('function');
expect(typeof props.votesFetched).to.be.equal('function');
expect(typeof props.delegatesFetched).to.be.equal('function');
});
});
4 changes: 2 additions & 2 deletions src/components/toaster/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { connect } from 'react-redux';
import Toaster from './toaster';
import { toastHidden } from '../../actions/toaster';

const mapStateToProps = state => ({
export const mapStateToProps = state => ({
toasts: state.toaster || [],
});

const mapDispatchToProps = dispatch => ({
export const mapDispatchToProps = dispatch => ({
hideToast: data => dispatch(toastHidden(data)),
});

Expand Down
1 change: 0 additions & 1 deletion src/components/toaster/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import Toaster from './toaster';
import ToasterHOC from './index';
import store from '../../store';


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

Expand Down
61 changes: 41 additions & 20 deletions src/components/toaster/toaster.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { expect } from 'chai';
import sinon from 'sinon';
import { mount } from 'enzyme';
import Toaster from './toaster';
import actionTypes from '../../constants/actions';
import { mapStateToProps, mapDispatchToProps } from './index';

describe('Toaster', () => {
const toasts = [{
Expand All @@ -12,36 +14,55 @@ describe('Toaster', () => {
}];
const toasterProps = {
toasts,
hideToast: sinon.spy(),
hideToast: () => {},
};

const hideToastSpy = sinon.spy(toasterProps, 'hideToast');

it('renders <Snackbar /> component from react-toolbox', () => {
const wrapper = mount(<Toaster {...toasterProps} />);
expect(wrapper.find('Snackbar')).to.have.length(1);
});

describe('hideToast', () => {
it('hides the toast and after the animation ends calls this.props.hideToast()', () => {
document.body.prepend(document.createElement('div'));
const clock = sinon.useFakeTimers({
toFake: ['setTimeout', 'clearTimeout', 'Date'],
});
mount(<Toaster {...toasterProps} />, { attachTo: document.body.firstChild });
let toastElement = document.getElementsByClassName('toast');
it('hides the toast and after the animation ends calls this.props.hideToast()', () => {
document.body.prepend(document.createElement('div'));
const clock = sinon.useFakeTimers({
toFake: ['setTimeout', 'clearTimeout', 'Date'],
});
mount(<Toaster {...toasterProps} />, { attachTo: document.body.firstChild });
let toastElement = document.getElementsByClassName('toast');

// check if the toast is activated
expect(toastElement.length > 0 &&
toastElement[0].className.indexOf('active') > 0)
.to.equal(true);
// check if the toast is activated
expect(toastElement.length > 0 &&
toastElement[0].className.indexOf('active') > 0)
.to.equal(true);

clock.tick(4510);
clock.tick(4510);

// check if the toast is deactivated
toastElement = document.getElementsByClassName('toast');
// check if the toast is deactivated
toastElement = document.getElementsByClassName('toast');

// check if hideToast is called
expect(toasterProps.hideToast).to.have.been.calledWith(toasts[0]);
clock.restore();
});
// check if hideToast is called
expect(hideToastSpy).to.have.been.calledWith(toasts[0]);

hideToastSpy.restore();

clock.restore();
});

it('hideToast dispatches toastHidden action', () => {
const dispatchSpy = sinon.spy();
const { hideToast } = mapDispatchToProps(dispatchSpy);
const payload = [{ toast1: {} }];
hideToast(payload);
const dispatchSpyCallArgs = dispatchSpy.args[0][0];
expect(dispatchSpyCallArgs.type).to.be.eql(actionTypes.toastHidden);
expect(dispatchSpyCallArgs.data).to.be.eql(payload);
});

it('inits with empty toasts when empty state', () => {
const initialState = {};
const toastsState = mapStateToProps(initialState);
expect(toastsState.toasts.length).to.be.equal(0);
});
});
7 changes: 7 additions & 0 deletions src/components/transactions/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ describe('TransactionsHOC', () => {
};
const account = { address: '16313739661670634666L' };
const peers = { data: {} };
const voting = {
votes: {},
delegates: [],
totalDelegates: [],
refresh: false,
};

beforeEach(() => {
store.getState = () => ({
peers,
transactions,
account,
loading: [],
voting,
});
wrapper = mount(<Provider store={store}><Router><TransactionsHOC /></Router></Provider>, {
context: { store, history, i18n },
Expand Down