From 4c6c0eeb0c731a07a588753ffbaa7e1f9eedc564 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Wed, 30 Aug 2017 12:30:40 +0200 Subject: [PATCH 1/5] Add a middleware that updates delegate on delegateRegistration --- src/constants/transactionTypes.js | 7 +++ src/store/middlewares/delegateRegistration.js | 23 ++++++++ .../middlewares/delegateRegistration.test.js | 59 +++++++++++++++++++ src/store/middlewares/index.js | 2 + 4 files changed, 91 insertions(+) create mode 100644 src/constants/transactionTypes.js create mode 100644 src/store/middlewares/delegateRegistration.js create mode 100644 src/store/middlewares/delegateRegistration.test.js diff --git a/src/constants/transactionTypes.js b/src/constants/transactionTypes.js new file mode 100644 index 000000000..ea2ac9f9a --- /dev/null +++ b/src/constants/transactionTypes.js @@ -0,0 +1,7 @@ +export default { + send: 0, + setSecondPassphrase: 1, + registerDelegate: 2, + vote: 3, +}; + diff --git a/src/store/middlewares/delegateRegistration.js b/src/store/middlewares/delegateRegistration.js new file mode 100644 index 000000000..38b4e2bc7 --- /dev/null +++ b/src/store/middlewares/delegateRegistration.js @@ -0,0 +1,23 @@ +import { getDelegate } from '../../utils/api/delegate'; +import { accountLoggedIn } from '../../actions/account'; +import actionTypes from '../../constants/actions'; +import transactionTypes from '../../constants/transactionTypes'; + +const delegateRegistrationMiddleware = store => next => (action) => { + if (action.type === actionTypes.transactionsUpdated) { + const delegateRegistrationTx = action.data.confirmed.filter( + transaction => transaction.type === transactionTypes.registerDelegate)[0]; + const state = store.getState(); + + if (delegateRegistrationTx) { + getDelegate(state.peers.data, state.account.publicKey) + .then((delegateData) => { + store.dispatch(accountLoggedIn(Object.assign({}, + { delegate: delegateData.delegate, isDelegate: true }))); + }); + } + } + return next(action); +}; + +export default delegateRegistrationMiddleware; diff --git a/src/store/middlewares/delegateRegistration.test.js b/src/store/middlewares/delegateRegistration.test.js new file mode 100644 index 000000000..de7b9a206 --- /dev/null +++ b/src/store/middlewares/delegateRegistration.test.js @@ -0,0 +1,59 @@ +import { expect } from 'chai'; +import { spy, stub } from 'sinon'; +import middleware from './delegateRegistration'; +import actionTypes from '../../constants/actions'; +import * as delegateApi from '../../utils/api/delegate'; +import transactionTypes from '../../constants/transactionTypes'; + +describe('Login middleware', () => { + let store; + let next; + const transactionsUpdatedAction = { + type: actionTypes.transactionsUpdated, + data: { + confirmed: [{ + type: transactionTypes.registerDelegate, + }], + }, + }; + + beforeEach(() => { + next = spy(); + store = stub(); + store.getState = () => ({ + peers: { + data: {}, + }, + account: {}, + }); + store.dispatch = spy(); + }); + + it(`should just pass action along for all actions except ${actionTypes.activePeerSet}`, () => { + const sampleAction = { + type: 'SAMPLE_TYPE', + data: 'SAMPLE_DATA', + }; + middleware(store)(next)(sampleAction); + expect(next).to.have.been.calledWith(sampleAction); + }); + + it(`should fetch delegate info on ${actionTypes.transactionsUpdated} action if action.data.confirmed contains delegateRegistration transactions`, () => { + const delegateApiMock = stub(delegateApi, 'getDelegate').returnsPromise().resolves({ success: true, delegate: {} }); + + middleware(store)(next)(transactionsUpdatedAction); + expect(store.dispatch).to.have.been.calledWith(); + + delegateApiMock.restore(); + }); + + it(`should not fetch delegate info on ${actionTypes.transactionsUpdated} action if action.data.confirmed does not contain delegateRegistration transactions`, () => { + const delegateApiMock = stub(delegateApi, 'getDelegate').returnsPromise().resolves({ success: true, delegate: {} }); + transactionsUpdatedAction.data.confirmed[0].type = transactionTypes.send; + + middleware(store)(next)(transactionsUpdatedAction); + expect(store.dispatch).to.not.have.been.calledWith(); + + delegateApiMock.restore(); + }); +}); diff --git a/src/store/middlewares/index.js b/src/store/middlewares/index.js index 6899dc017..7ecaefc57 100644 --- a/src/store/middlewares/index.js +++ b/src/store/middlewares/index.js @@ -6,6 +6,7 @@ import addedTransactionMiddleware from './addedTransaction'; import loadingBarMiddleware from './loadingBar'; import offlineMiddleware from './offline'; import notificationMiddleware from './notification'; +import delegateRegistrationMiddleware from './delegateRegistration'; export default [ thunk, @@ -16,4 +17,5 @@ export default [ loadingBarMiddleware, offlineMiddleware, notificationMiddleware, + delegateRegistrationMiddleware, ]; From eaa1676635fcc403049481702c11427fea1ca436 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Wed, 30 Aug 2017 12:36:13 +0200 Subject: [PATCH 2/5] Refactor transaction amount to use transactionTypes consts --- src/components/transactions/amount.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/transactions/amount.js b/src/components/transactions/amount.js index 363b8ed34..1f817b648 100644 --- a/src/components/transactions/amount.js +++ b/src/components/transactions/amount.js @@ -3,18 +3,20 @@ import styles from './transactions.css'; import LiskAmount from '../liskAmount'; import { TooltipWrapper } from '../timestamp'; import ClickToSend from '../clickToSend'; +import transactionTypes from '../../constants/transactionTypes'; const Amount = (props) => { const params = {}; - if (props.value.type === 0 && + if (props.value.type === transactionTypes.send && props.value.senderId === props.value.recipientId) { params.className = 'grayButton'; } else if (props.value.senderId !== props.address) { params.className = 'inButton'; - } else if (props.value.type !== 0 || props.value.recipientId !== props.address) { + } else if (props.value.type !== transactionTypes.send || + props.value.recipientId !== props.address) { params.className = 'outButton'; - params.tooltipText = props.value.type === 0 ? 'Repeat the transaction' : undefined; - params.clickToSendEnabled = props.value.type === 0; + params.tooltipText = props.value.type === transactionTypes.send ? 'Repeat the transaction' : undefined; + params.clickToSendEnabled = props.value.type === transactionTypes.send; } return Date: Wed, 30 Aug 2017 13:04:19 +0200 Subject: [PATCH 3/5] Add e2e test that delegate data is delegateRegistration --- features/menu.feature | 3 +++ features/step_definitions/generic.step.js | 4 ++++ src/components/account/address.js | 2 +- src/components/dialog/alert.js | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/features/menu.feature b/features/menu.feature index e7cade302..6d33e8c7e 100644 --- a/features/menu.feature +++ b/features/menu.feature @@ -30,6 +30,9 @@ Feature: Top right menu And I fill in "test" to "username" field And I click "register button" Then I should see alert dialog with title "Success" and text "Delegate registration was successfully submitted. It can take several seconds before it is processed." + And I click "ok button" + And I wait 15 seconds + And I should see text "test" in "delegate name" element Scenario: should not allow to register a delegate again Given I'm logged in as "delegate" diff --git a/features/step_definitions/generic.step.js b/features/step_definitions/generic.step.js index 30e8a28ae..3d67a7d95 100644 --- a/features/step_definitions/generic.step.js +++ b/features/step_definitions/generic.step.js @@ -31,6 +31,10 @@ defineSupportCode(({ Given, When, Then, setDefaultTimeout }) => { waitForElemAndSendKeys(`${selectorClass} input, ${selectorClass} textarea`, secondPassphrase, callback); }); + When('I wait {seconds} seconds', (seconds, callback) => { + browser.sleep(seconds * 1000).then(callback); + }); + Then('I should see "{value}" in "{fieldName}" field', (value, fieldName, callback) => { const elem = element(by.css(`.${fieldName.replace(/ /g, '-')} input, .${fieldName.replace(/ /g, '-')} textarea`)); diff --git a/src/components/account/address.js b/src/components/account/address.js index 679c92a57..ea8dd8ab0 100644 --- a/src/components/account/address.js +++ b/src/components/account/address.js @@ -6,7 +6,7 @@ const Address = (props) => { const title = props.isDelegate ? 'Delegate' : 'Address'; const content = props.isDelegate ? (
-

+

{props.delegate.username}

diff --git a/src/components/dialog/alert.js b/src/components/dialog/alert.js index 538255f1f..bcb76bd43 100644 --- a/src/components/dialog/alert.js +++ b/src/components/dialog/alert.js @@ -9,7 +9,7 @@ const Alert = props => (

-
); From ebae299cef593251508f6b40fd6df8ed176a09a3 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Thu, 31 Aug 2017 14:38:20 +0200 Subject: [PATCH 4/5] Move delegateRegistrationMiddleware to accountMiddleware --- src/store/middlewares/account.js | 29 +++++++-- src/store/middlewares/account.test.js | 34 +++++++++-- src/store/middlewares/delegateRegistration.js | 23 -------- .../middlewares/delegateRegistration.test.js | 59 ------------------- src/store/middlewares/index.js | 2 - 5 files changed, 54 insertions(+), 93 deletions(-) delete mode 100644 src/store/middlewares/delegateRegistration.js delete mode 100644 src/store/middlewares/delegateRegistration.test.js diff --git a/src/store/middlewares/account.js b/src/store/middlewares/account.js index 3a5c0589d..f972de3a8 100644 --- a/src/store/middlewares/account.js +++ b/src/store/middlewares/account.js @@ -1,9 +1,11 @@ import { getAccountStatus, getAccount, transactions } from '../../utils/api/account'; -import { accountUpdated } from '../../actions/account'; +import { accountUpdated, accountLoggedIn } from '../../actions/account'; import { transactionsUpdated } from '../../actions/transactions'; import { activePeerUpdate } from '../../actions/peers'; import actionTypes from '../../constants/actions'; import { fetchAndUpdateForgedBlocks } from '../../actions/forging'; +import { getDelegate } from '../../utils/api/delegate'; +import transactionTypes from '../../constants/transactionTypes'; const updateAccountData = next => (store) => { // eslint-disable-line const { peers, account } = store.getState(); @@ -12,10 +14,10 @@ const updateAccountData = next => (store) => { // eslint-disable-line if (result.balance !== account.balance) { const maxBlockSize = 25; transactions(peers.data, account.address, maxBlockSize) - .then(response => next(transactionsUpdated({ - confirmed: response.transactions, - count: parseInt(response.count, 10), - }))); + .then(response => next(transactionsUpdated({ + confirmed: response.transactions, + count: parseInt(response.count, 10), + }))); if (account.isDelegate) { store.dispatch(fetchAndUpdateForgedBlocks({ activePeer: peers.data, @@ -35,6 +37,20 @@ const updateAccountData = next => (store) => { // eslint-disable-line }); }; +const delegateRegistration = (store, action) => { + const delegateRegistrationTx = action.data.confirmed.filter( + transaction => transaction.type === transactionTypes.registerDelegate)[0]; + const state = store.getState(); + + if (delegateRegistrationTx) { + getDelegate(state.peers.data, state.account.publicKey) + .then((delegateData) => { + store.dispatch(accountLoggedIn(Object.assign({}, + { delegate: delegateData.delegate, isDelegate: true }))); + }); + } +}; + const accountMiddleware = store => next => (action) => { next(action); const update = updateAccountData(next); @@ -42,6 +58,9 @@ const accountMiddleware = store => next => (action) => { case actionTypes.metronomeBeat: update(store); break; + case actionTypes.transactionsUpdated: + delegateRegistration(store, action); + break; default: break; } }; diff --git a/src/store/middlewares/account.test.js b/src/store/middlewares/account.test.js index 516914769..894f7a701 100644 --- a/src/store/middlewares/account.test.js +++ b/src/store/middlewares/account.test.js @@ -2,13 +2,22 @@ import { expect } from 'chai'; import { spy, stub } from 'sinon'; import middleware from './account'; import * as accountApi from '../../utils/api/account'; +import * as delegateApi from '../../utils/api/delegate'; import actionTypes from '../../constants/actions'; -// import * as forgingActions from '../../actions/forging'; +import transactionTypes from '../../constants/transactionTypes'; describe('Account middleware', () => { let store; let next; let state; + const transactionsUpdatedAction = { + type: actionTypes.transactionsUpdated, + data: { + confirmed: [{ + type: transactionTypes.registerDelegate, + }], + }, + }; beforeEach(() => { store = stub(); @@ -21,11 +30,11 @@ describe('Account middleware', () => { balance: 0, }, }; + store.getState = () => (state); next = spy(); }); it('should passes the action to next middleware', () => { - store.getState = () => (state); const expectedAction = { type: 'TEST_ACTION', }; @@ -35,7 +44,6 @@ describe('Account middleware', () => { }); it(`should call account API methods on ${actionTypes.metronomeBeat} action`, () => { - store.getState = () => (state); const stubGetAccount = stub(accountApi, 'getAccount').resolves({ balance: 0 }); const stubGetAccountStatus = stub(accountApi, 'getAccountStatus').resolves(true); @@ -49,7 +57,6 @@ describe('Account middleware', () => { }); it(`should call transactions API methods on ${actionTypes.metronomeBeat} action if account.balance changes`, () => { - store.getState = () => (state); const stubGetAccount = stub(accountApi, 'getAccount').resolves({ balance: 10e8 }); const stubTransactions = stub(accountApi, 'transactions').resolves(true); @@ -78,5 +85,24 @@ describe('Account middleware', () => { stubGetAccount.restore(); stubGetAccountStatus.restore(); }); + + it(`should fetch delegate info on ${actionTypes.transactionsUpdated} action if action.data.confirmed contains delegateRegistration transactions`, () => { + const delegateApiMock = stub(delegateApi, 'getDelegate').returnsPromise().resolves({ success: true, delegate: {} }); + + middleware(store)(next)(transactionsUpdatedAction); + expect(store.dispatch).to.have.been.calledWith(); + + delegateApiMock.restore(); + }); + + it(`should not fetch delegate info on ${actionTypes.transactionsUpdated} action if action.data.confirmed does not contain delegateRegistration transactions`, () => { + const delegateApiMock = stub(delegateApi, 'getDelegate').returnsPromise().resolves({ success: true, delegate: {} }); + transactionsUpdatedAction.data.confirmed[0].type = transactionTypes.send; + + middleware(store)(next)(transactionsUpdatedAction); + expect(store.dispatch).to.not.have.been.calledWith(); + + delegateApiMock.restore(); + }); }); diff --git a/src/store/middlewares/delegateRegistration.js b/src/store/middlewares/delegateRegistration.js deleted file mode 100644 index 38b4e2bc7..000000000 --- a/src/store/middlewares/delegateRegistration.js +++ /dev/null @@ -1,23 +0,0 @@ -import { getDelegate } from '../../utils/api/delegate'; -import { accountLoggedIn } from '../../actions/account'; -import actionTypes from '../../constants/actions'; -import transactionTypes from '../../constants/transactionTypes'; - -const delegateRegistrationMiddleware = store => next => (action) => { - if (action.type === actionTypes.transactionsUpdated) { - const delegateRegistrationTx = action.data.confirmed.filter( - transaction => transaction.type === transactionTypes.registerDelegate)[0]; - const state = store.getState(); - - if (delegateRegistrationTx) { - getDelegate(state.peers.data, state.account.publicKey) - .then((delegateData) => { - store.dispatch(accountLoggedIn(Object.assign({}, - { delegate: delegateData.delegate, isDelegate: true }))); - }); - } - } - return next(action); -}; - -export default delegateRegistrationMiddleware; diff --git a/src/store/middlewares/delegateRegistration.test.js b/src/store/middlewares/delegateRegistration.test.js deleted file mode 100644 index de7b9a206..000000000 --- a/src/store/middlewares/delegateRegistration.test.js +++ /dev/null @@ -1,59 +0,0 @@ -import { expect } from 'chai'; -import { spy, stub } from 'sinon'; -import middleware from './delegateRegistration'; -import actionTypes from '../../constants/actions'; -import * as delegateApi from '../../utils/api/delegate'; -import transactionTypes from '../../constants/transactionTypes'; - -describe('Login middleware', () => { - let store; - let next; - const transactionsUpdatedAction = { - type: actionTypes.transactionsUpdated, - data: { - confirmed: [{ - type: transactionTypes.registerDelegate, - }], - }, - }; - - beforeEach(() => { - next = spy(); - store = stub(); - store.getState = () => ({ - peers: { - data: {}, - }, - account: {}, - }); - store.dispatch = spy(); - }); - - it(`should just pass action along for all actions except ${actionTypes.activePeerSet}`, () => { - const sampleAction = { - type: 'SAMPLE_TYPE', - data: 'SAMPLE_DATA', - }; - middleware(store)(next)(sampleAction); - expect(next).to.have.been.calledWith(sampleAction); - }); - - it(`should fetch delegate info on ${actionTypes.transactionsUpdated} action if action.data.confirmed contains delegateRegistration transactions`, () => { - const delegateApiMock = stub(delegateApi, 'getDelegate').returnsPromise().resolves({ success: true, delegate: {} }); - - middleware(store)(next)(transactionsUpdatedAction); - expect(store.dispatch).to.have.been.calledWith(); - - delegateApiMock.restore(); - }); - - it(`should not fetch delegate info on ${actionTypes.transactionsUpdated} action if action.data.confirmed does not contain delegateRegistration transactions`, () => { - const delegateApiMock = stub(delegateApi, 'getDelegate').returnsPromise().resolves({ success: true, delegate: {} }); - transactionsUpdatedAction.data.confirmed[0].type = transactionTypes.send; - - middleware(store)(next)(transactionsUpdatedAction); - expect(store.dispatch).to.not.have.been.calledWith(); - - delegateApiMock.restore(); - }); -}); diff --git a/src/store/middlewares/index.js b/src/store/middlewares/index.js index 7ecaefc57..6899dc017 100644 --- a/src/store/middlewares/index.js +++ b/src/store/middlewares/index.js @@ -6,7 +6,6 @@ import addedTransactionMiddleware from './addedTransaction'; import loadingBarMiddleware from './loadingBar'; import offlineMiddleware from './offline'; import notificationMiddleware from './notification'; -import delegateRegistrationMiddleware from './delegateRegistration'; export default [ thunk, @@ -17,5 +16,4 @@ export default [ loadingBarMiddleware, offlineMiddleware, notificationMiddleware, - delegateRegistrationMiddleware, ]; From 2503ba1575a5154c6b9872b1328aa288fb731ec5 Mon Sep 17 00:00:00 2001 From: Vit Stanislav Date: Fri, 1 Sep 2017 09:03:49 +0200 Subject: [PATCH 5/5] Replace next with store.dispatch for new actions --- src/store/middlewares/account.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/store/middlewares/account.js b/src/store/middlewares/account.js index f972de3a8..33628e478 100644 --- a/src/store/middlewares/account.js +++ b/src/store/middlewares/account.js @@ -7,14 +7,14 @@ import { fetchAndUpdateForgedBlocks } from '../../actions/forging'; import { getDelegate } from '../../utils/api/delegate'; import transactionTypes from '../../constants/transactionTypes'; -const updateAccountData = next => (store) => { // eslint-disable-line +const updateAccountData = (store) => { // eslint-disable-line const { peers, account } = store.getState(); getAccount(peers.data, account.address).then((result) => { if (result.balance !== account.balance) { const maxBlockSize = 25; transactions(peers.data, account.address, maxBlockSize) - .then(response => next(transactionsUpdated({ + .then(response => store.dispatch(transactionsUpdated({ confirmed: response.transactions, count: parseInt(response.count, 10), }))); @@ -27,13 +27,13 @@ const updateAccountData = next => (store) => { // eslint-disable-line })); } } - next(accountUpdated(result)); + store.dispatch(accountUpdated(result)); }); return getAccountStatus(peers.data).then(() => { - next(activePeerUpdate({ online: true })); + store.dispatch(activePeerUpdate({ online: true })); }).catch((res) => { - next(activePeerUpdate({ online: false, code: res.error.code })); + store.dispatch(activePeerUpdate({ online: false, code: res.error.code })); }); }; @@ -53,10 +53,9 @@ const delegateRegistration = (store, action) => { const accountMiddleware = store => next => (action) => { next(action); - const update = updateAccountData(next); switch (action.type) { case actionTypes.metronomeBeat: - update(store); + updateAccountData(store); break; case actionTypes.transactionsUpdated: delegateRegistration(store, action);