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

Isolate voting dialog - Closes #723 #748

Merged
merged 19 commits into from
Sep 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
42963b1
Refactor voting reducer
reyraa Sep 11, 2017
7e5ded5
Replace addToVotelsi and removeFromVotes with voteToggled
reyraa Sep 12, 2017
c0838c0
Minor fixings in reducer. tests updated
reyraa Sep 12, 2017
dbf1cc0
Update voting components according to changes in actions and reducers
reyraa Sep 12, 2017
8e4b6e3
Add delegatesFetched action. - Move votes dict normalization to actio…
reyraa Sep 15, 2017
9574797
Pass the list of delegates of votes to votingDialog using connect
reyraa Sep 15, 2017
d961ed9
Use new actions and delegtes/vote lists in votingDialog
reyraa Sep 15, 2017
f356bd0
Improvements in voting reducer to reduce the number of iterations req…
reyraa Sep 15, 2017
1b0efbc
Update utitilies and adapt its unit tests
reyraa Sep 15, 2017
eb5e7e5
Adapt actions unit tests
reyraa Sep 15, 2017
092e618
- Store the list of delegates and votes on store. -Update component a…
reyraa Sep 15, 2017
fb5df5e
- Use redux store to access delegates and votes. update the actions. …
reyraa Sep 15, 2017
287594f
Fix conflicts
reyraa Sep 15, 2017
56f8bb6
Fix search and remove unnecessary setStates
reyraa Sep 17, 2017
80e5d21
Fix a UI issue with autocomplete
reyraa Sep 18, 2017
ce264a6
Rename some variables to reflect their new specifications
reyraa Sep 18, 2017
17e302b
replace clearedVoteLists with votesUpdates to update votes individually
reyraa Sep 18, 2017
cffbe4f
Compare new and old votes to keep the list update
reyraa Sep 18, 2017
5971aec
Use votesUpdated after new vote transaction confirmed
reyraa Sep 18, 2017
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
85 changes: 69 additions & 16 deletions src/actions/voting.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import actionTypes from '../constants/actions';
import { vote } from '../utils/api/delegate';
import { vote, listAccountDelegates, listDelegates } from '../utils/api/delegate';
import { transactionAdded } from './transactions';
import { errorAlertDialogDisplayed } from './dialog';
import Fees from '../constants/fees';
Expand All @@ -14,16 +14,54 @@ export const pendingVotesAdded = () => ({
/**
* Remove all data from the list of voted delegates and list of unvoted delegates
*/
export const clearVoteLists = () => ({
type: actionTypes.votesCleared,
export const votesUpdated = data => ({
type: actionTypes.votesUpdated,
data,
});

/**
*
* Add data to the list of voted delegates
*/
export const votePlaced = ({ activePeer, account, votedList, unvotedList, secondSecret }) =>
export const votesAdded = data => ({
type: actionTypes.votesAdded,
data,
});

/**
* Add data to the list of all delegates
*/
export const delegatesAdded = data => ({
type: actionTypes.delegatesAdded,
data,
});

/**
* Toggles account's vote for the given delegate
*/
export const voteToggled = data => ({
type: actionTypes.voteToggled,
data,
});

/**
* Makes Api call to register votes
* Adds pending state and then after the duration of one round
* cleans the pending state
*/
export const votePlaced = ({ activePeer, account, votes, secondSecret }) =>
(dispatch) => {
// Make the Api call
const votedList = [];
const unvotedList = [];

Object.keys(votes).forEach((username) => {
/* istanbul ignore else */
if (!votes[username].confirmed && votes[username].unconfirmed) {
votedList.push(votes[username].publicKey);
} else if (votes[username].confirmed && !votes[username].unconfirmed) {
unvotedList.push(votes[username].publicKey);
}
});

vote(
activePeer,
account.passphrase,
Expand Down Expand Up @@ -53,17 +91,32 @@ export const votePlaced = ({ activePeer, account, votedList, unvotedList, second
};

/**
* Add data to the list of voted delegates
* Gets the list of delegates current account has voted for
*
*/
export const addedToVoteList = data => ({
type: actionTypes.addedToVoteList,
data,
});
export const votesFetched = ({ activePeer, address, type }) =>
(dispatch) => {
listAccountDelegates(activePeer, address).then(({ delegates }) => {
if (type === 'update') {
dispatch(votesUpdated({ list: delegates }));
} else {
dispatch(votesAdded({ list: delegates }));
}
});
};

/**
* Remove data from the list of voted delegates
* Gets list of all delegates
*/
export const removedFromVoteList = data => ({
type: actionTypes.removedFromVoteList,
data,
});
export const delegatesFetched = ({ activePeer, q, offset, refresh }) =>
(dispatch) => {
listDelegates(
activePeer, {
offset,
limit: '100',
q,
},
).then(({ delegates, totalCount }) => {
dispatch(delegatesAdded({ list: delegates, totalDelegates: totalCount, refresh }));
});
};
106 changes: 84 additions & 22 deletions src/actions/voting.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,60 @@ import { expect } from 'chai';
import sinon from 'sinon';
import actionTypes from '../constants/actions';
import {
addedToVoteList,
removedFromVoteList,
clearVoteLists,
pendingVotesAdded,
votePlaced,
pendingVotesAdded,
votesUpdated,
votesAdded,
voteToggled,
votePlaced,
votesFetched,
delegatesFetched,
delegatesAdded,
} from './voting';
import Fees from '../constants/fees';
import { transactionAdded } from './transactions';
import { errorAlertDialogDisplayed } from './dialog';
import * as delegateApi from '../utils/api/delegate';

const delegateList = [
{ username: 'username1', publicKey: '123HG3452245L' },
{ username: 'username2', publicKey: '123HG3522345L' },
];

describe('actions: voting', () => {
describe('addedToVoteList', () => {
it('should create an action to add data to vote list', () => {
describe('voteToggled', () => {
it('should create an action to add data to toggle the vote status for any given delegate', () => {
const data = {
label: 'dummy',
};
const expectedAction = {
data,
type: actionTypes.addedToVoteList,
type: actionTypes.voteToggled,
};

expect(addedToVoteList(data)).to.be.deep.equal(expectedAction);
expect(voteToggled(data)).to.be.deep.equal(expectedAction);
});
});

describe('removedFromVoteList', () => {
describe('votesAdded', () => {
it('should create an action to remove data from vote list', () => {
const data = {
label: 'dummy',
};
const data = delegateList;
const expectedAction = {
data,
type: actionTypes.removedFromVoteList,
type: actionTypes.votesAdded,
};

expect(removedFromVoteList(data)).to.be.deep.equal(expectedAction);
expect(votesAdded(data)).to.be.deep.equal(expectedAction);
});
});

describe('clearVoteLists', () => {
it('should create an action to remove all pending rows from vote list', () => {
describe('votesUpdated', () => {
it('should create an action to update the votes dictionary', () => {
const expectedAction = {
type: actionTypes.votesCleared,
type: actionTypes.votesUpdated,
data: { list: delegateList },
};
expect(clearVoteLists()).to.be.deep.equal(expectedAction);
const createdAction = votesUpdated({ list: delegateList });
expect(createdAction).to.be.deep.equal(expectedAction);
});
});

Expand All @@ -67,12 +75,14 @@ describe('actions: voting', () => {
address: 'test_address',
};
const activePeer = {};
const votedList = [];
const unvotedList = [];
const secondSecret = null;
const votes = {
username1: { publicKey: 'sample_key', confirmed: true, unconfirmed: false },
username2: { publicKey: 'sample_key', confirmed: false, unconfirmed: true },
};

const actionFunction = votePlaced({
activePeer, account, votedList, unvotedList, secondSecret,
activePeer, account, votes, secondSecret,
});
let dispatch;

Expand Down Expand Up @@ -120,4 +130,56 @@ describe('actions: voting', () => {
expect(dispatch).to.have.been.calledWith(expectedAction);
});
});

describe('votesFetched', () => {
const data = {
activePeer: {},
address: '8096217735672704724L',
};
const delegates = delegateList;
const actionFunction = votesFetched(data);

it('should create an action function', () => {
expect(typeof actionFunction).to.be.deep.equal('function');
});

it.skip('should dispatch votesAdded action if resolved', () => {
const delegateApiMock = sinon.stub(delegateApi, 'listAccountDelegates');
const dispatch = sinon.spy();

delegateApiMock.returnsPromise().resolves({ delegates });
const expectedAction = { list: delegates };

actionFunction(dispatch);
expect(dispatch).to.have.been.calledWith(votesAdded(expectedAction));
delegateApiMock.restore();
});
});

describe('delegatesFetched', () => {
const data = {
activePeer: {},
q: '',
offset: 0,
refresh: true,
};
const delegates = delegateList;
const actionFunction = delegatesFetched(data);

it('should create an action function', () => {
expect(typeof actionFunction).to.be.deep.equal('function');
});

it('should dispatch delegatesAdded action if resolved', () => {
const delegateApiMock = sinon.stub(delegateApi, 'listDelegates');
const dispatch = sinon.spy();

delegateApiMock.returnsPromise().resolves({ delegates, totalCount: 10 });
const expectedAction = { list: delegates, totalDelegates: 10, refresh: true };

actionFunction(dispatch);
expect(dispatch).to.have.been.calledWith(delegatesAdded(expectedAction));
delegateApiMock.restore();
});
});
});
9 changes: 4 additions & 5 deletions src/components/voteDialog/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { connect } from 'react-redux';
import { votePlaced, addedToVoteList, removedFromVoteList } from '../../actions/voting';
import { votePlaced, voteToggled } from '../../actions/voting';
import VoteDialog from './voteDialog';

const mapStateToProps = state => ({
votedList: state.voting.votedList.filter(item => !item.pending),
unvotedList: state.voting.unvotedList.filter(item => !item.pending),
votes: state.voting.votes,
delegates: state.voting.delegates,
account: state.account,
activePeer: state.peers.data,
});

const mapDispatchToProps = dispatch => ({
votePlaced: data => dispatch(votePlaced(data)),
addedToVoteList: data => dispatch(addedToVoteList(data)),
removedFromVoteList: data => dispatch(removedFromVoteList(data)),
voteToggled: data => dispatch(voteToggled(data)),
});

export default connect(mapStateToProps, mapDispatchToProps)(VoteDialog);
39 changes: 16 additions & 23 deletions src/components/voteDialog/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,26 @@ const ordinaryAccount = {
secondSignature: 0,
balance: 10e8,
};
const votedList = [
const delegates = [
{
username: 'yashar',
publicKey: 'sample_key',
},
{
username: 'tom',
publicKey: 'sample_key',
},
];
const unvotedList = [
{
username: 'john',
},
{
username: 'test',
},
];
const votes = {
john: { confirmed: false, unconfirmed: true, publicKey: 'sample_key' },
yashar: { confirmed: true, unconfirmed: false, publicKey: 'sample_key' },

};
const store = configureMockStore([])({
account: ordinaryAccount,
voting: {
votedList: [...votedList, { pending: true, username: 'pending' }],
unvotedList: [...unvotedList, { pending: true, username: 'pending2' }],
votes,
delegates,
},
peers: { data: {} },
});
Expand All @@ -59,22 +58,16 @@ describe('VoteDialog HOC', () => {
it('should pass appropriate properties to VoteDialog', () => {
const confirmVotesProps = wrapper.find('VoteDialog').props();

expect(confirmVotesProps.votedList).to.be.deep.equal(votedList);
expect(confirmVotesProps.unvotedList).to.be.deep.equal(unvotedList);
expect(confirmVotesProps.votes).to.be.equal(votes);
expect(confirmVotesProps.delegates).to.be.equal(delegates);
expect(confirmVotesProps.account).to.be.equal(ordinaryAccount);
expect(confirmVotesProps.activePeer).to.deep.equal({});
expect(typeof confirmVotesProps.votePlaced).to.be.equal('function');
});

it('should bind addedToVoteList action to VoteDialog props.addedToVoteList', () => {
const actionsSpy = sinon.spy(votingActions, 'addedToVoteList');
wrapper.find('VoteDialog').props().addedToVoteList([]);
expect(actionsSpy).to.be.calledWith();
expect(typeof confirmVotesProps.voteToggled).to.be.equal('function');
});

it('should bind removedFromVoteList action to VoteDialog props.removedFromVoteList', () => {
const actionsSpy = sinon.spy(votingActions, 'removedFromVoteList');
wrapper.find('VoteDialog').props().removedFromVoteList([]);
it('should bind voteToggled action to VoteDialog props.voteToggled', () => {
const actionsSpy = sinon.spy(votingActions, 'voteToggled');
wrapper.find('VoteDialog').props().voteToggled([]);
expect(actionsSpy).to.be.calledWith();
});
});
Loading