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

Commit

Permalink
Merge branch 'development' into 531-unit-test-coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
slaweet committed Aug 15, 2017
2 parents 292b350 + 54642eb commit 865b414
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 94 deletions.
34 changes: 0 additions & 34 deletions src/components/account/accountComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import grid from 'flexboxgrid/dist/flexboxgrid.css';
import styles from './account.css';
import Address from './address';
import LiskAmount from '../liskAmount';
import { getAccountStatus, getAccount, transactions } from '../../utils/api/account';
import ClickToSend from '../send/clickToSend';
import { toRawLsk } from '../../utils/lsk';

Expand All @@ -13,39 +12,6 @@ import { toRawLsk } from '../../utils/lsk';
* @param {object} props - include properties of component
*/
class AccountComponent extends React.Component {
constructor(props) {
super(props);
this.update = this.update.bind(this);
}
componentDidMount() {
this.update();
document.addEventListener('beat', this.update);
}

componentWillUnmount() {
document.removeEventListener('beat', this.update);
}

update() {
getAccount(this.props.peers.data, this.props.account.address).then((result) => {
if (result.balance !== this.props.account.balance) {
const maxBlockSize = 25;
transactions(this.props.peers.data, this.props.account.address, maxBlockSize)
.then((res) => {
this.props.onTransactionsUpdated(res.transactions);
});
}
this.props.onAccountUpdated(result);
});

const { onActivePeerUpdated } = this.props;
return getAccountStatus(this.props.peers.data).then(() => {
onActivePeerUpdated({ online: true });
}).catch(() => {
onActivePeerUpdated({ online: false });
});
}

render() {
const status = (this.props.peers.status && this.props.peers.status.online) ?
<i className="material-icons online">check</i>
Expand Down
40 changes: 0 additions & 40 deletions src/components/account/accountComponent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,44 +57,4 @@ describe('AccountComponent', () => {
</Provider>);
expect(wrapper.find('.balance').find(ClickToSend)).to.have.lengthOf(1);
});

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

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

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

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

it('calls props.onActivePeerUpdated', () => {
accountApiMock.expects('getAccountStatus').resolves({ success: true });
mount(<Provider store={store}><AccountComponent {...props} /></Provider>);
// 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 });
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 });
mount(<Provider store={store}><AccountComponent {...props} /></Provider>);
// TODO: this doesn't work for some reason
// expect(props.onAccountUpdated).to.have.been.calledWith();
});
});
});
5 changes: 0 additions & 5 deletions src/components/app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,11 @@ import Transactions from '../transactions';
import Voting from '../voting';
import Forging from '../forging';
import styles from './app.css';
import Metronome from '../../utils/metronome';
import Dialog from '../dialog';
import Toaster from '../toaster';
import Tabs from '../tabs';
import LoadingBar from '../loadingBar';

// start dispatching sync ticks
const metronome = new Metronome();
metronome.init();

const App = () => (
<section className={styles['body-wrapper']}>
<Header />
Expand Down
1 change: 1 addition & 0 deletions src/constants/actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const actionTypes = {
metronomeBeat: 'METRONOME_BEAT',
accountUpdated: 'ACCOUNT_UPDATED',
accountLoggedOut: 'ACCOUNT_LOGGED_OUT',
activePeerSet: 'ACTIVE_PEER_SET',
Expand Down
2 changes: 1 addition & 1 deletion src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createStore, combineReducers, applyMiddleware } from 'redux';
import * as reducers from './reducers';
import middleWares from './middlewares';
import env from '../constants/env';

// Create Logger if not in production mode
const middleWares = [];
// ignore this in coverage as it is hard to test and does not run in production
/* istanbul ignore if */
if (env.development) {
Expand Down
38 changes: 38 additions & 0 deletions src/store/middlewares/account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { getAccountStatus, getAccount, transactions } from '../../utils/api/account';
import { accountUpdated } from '../../actions/account';
import { transactionsUpdated } from '../../actions/transactions';
import { activePeerUpdate } from '../../actions/peers';
import actionsType from '../../constants/actions';

const updateAccountData = next => (store) => { // eslint-disable-line
const { peers, account } = store.getState();
// TODO remove if statement when the beat event wiil be launched after loged in
if (peers.data && account) {
getAccount(peers.data, account.address).then((result) => {
if (result.balance !== account.balance) {
const maxBlockSize = 25;
transactions(peers.data, account.address, maxBlockSize)
.then(res => next(transactionsUpdated(res.transactions)));
}
next(accountUpdated(result));
});
return getAccountStatus(peers.data).then(() => {
next(activePeerUpdate({ online: true }));
}).catch(() => {
next(activePeerUpdate({ online: false }));
});
}
};

const accountMiddleware = store => next => (action) => {
next(action);
const update = updateAccountData(next);
switch (action.type) {
case actionsType.metronomeBeat:
update(store);
break;
default: break;
}
};

export default accountMiddleware;
44 changes: 44 additions & 0 deletions src/store/middlewares/account.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { expect } from 'chai';
import { spy, stub } from 'sinon';
import middleware from './account';
import * as accountApi from '../../utils/api/account';
import actionType from '../../constants/actions';

describe('Account middleware', () => {
let store;
let next;

beforeEach(() => {
store = stub();
store.getState = () => ({
peers: {
data: {},
},
account: {},
});
next = spy();
});

it('should passes the action to next middleware', () => {
const expectedAction = {
type: 'TEST_ACTION',
};

middleware(store)(next)(expectedAction);
expect(next).to.have.been.calledWith(expectedAction);
});

it(`should call account API methods on ${actionType.metronomeBeat} action`, () => {
const stubGetAccount = stub(accountApi, 'getAccount').resolves(true);
const stubGetAccountStatus = stub(accountApi, 'getAccountStatus').resolves(true);

middleware(store)(next)({ type: actionType.metronomeBeat });

expect(stubGetAccount).to.have.been.calledWith();
expect(stubGetAccountStatus).to.have.been.calledWith();

stubGetAccount.restore();
stubGetAccountStatus.restore();
});
});

7 changes: 7 additions & 0 deletions src/store/middlewares/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import metronomeMiddleware from './metronome';
import accountMiddleware from './account';

export default [
metronomeMiddleware,
accountMiddleware,
];
19 changes: 19 additions & 0 deletions src/store/middlewares/metronome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import MetronomeService from '../../utils/metronome';
import actionTypes from '../../constants/actions';

const metronomeMiddleware = (store) => {
const metronome = new MetronomeService(store.dispatch);
// TODO: call metronome.init on login success event
metronome.init();
return next => (action) => {
switch (action.type) {
case actionTypes.accountLoggedOut:
metronome.terminate();
break;
default: break;
}
next(action);
};
};

export default metronomeMiddleware;
45 changes: 45 additions & 0 deletions src/store/middlewares/metronome.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { expect } from 'chai';
import { spy, stub } from 'sinon';
import middleware from './metronome';
import actionTypes from '../../constants/actions';
import * as MetronomeService from '../../utils/metronome';

describe('Metronome middleware', () => {
let store;
let next;

beforeEach(() => {
next = spy();
store = stub();
store.dispatch = spy();
});

it('should call Metronome constructor', () => {
const metronome = spy(MetronomeService, 'default');
middleware(store);
expect(metronome.calledWithNew()).to.equal(true);
expect(metronome).to.have.been.calledWith(store.dispatch);
metronome.restore();
});

it('should call Metronome init method', () => {
const spyFn = spy(MetronomeService.default.prototype, 'init');
middleware(store);
expect(spyFn).to.have.been.calledWith();
});

it(`should call metronome.terminate on ${actionTypes.accountLoggedOut} action`, () => {
const spyFn = spy(MetronomeService.default.prototype, 'terminate');
middleware(store)(next)({ type: actionTypes.accountLoggedOut });
expect(spyFn).to.have.been.calledWith();
});

it('should passes the action to next middleware', () => {
const expectedAction = {
type: 'TEST_ACTION',
};
middleware(store)(next)(expectedAction);
expect(next).to.have.been.calledWith(expectedAction);
});
});

16 changes: 8 additions & 8 deletions src/utils/metronome.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// import { ipcMain as ipc, BrowserWindow } from 'electron';
import { SYNC_ACTIVE_INTERVAL, SYNC_INACTIVE_INTERVAL } from '../constants/api';
import env from '../constants/env';
import actionsType from '../constants/actions';

class Metronome {
constructor() {
constructor(dispatchFn) {
this.interval = SYNC_ACTIVE_INTERVAL;
this.lastBeat = new Date();
this.factor = 0;
this.running = false;
this.dispatchFn = dispatchFn;
}

/**
Expand All @@ -19,13 +21,11 @@ class Metronome {
* @memberOf Metronome
* @private
*/
static _dispatch(lastBeat, now, factor) {
const ev = new Event('beat', {
factor,
lastBeat,
now,
_dispatch(lastBeat, now, factor) {
this.dispatchFn({
type: actionsType.metronomeBeat,
data: { lastBeat, now, factor },
});
document.dispatchEvent(ev);
}

/**
Expand All @@ -39,7 +39,7 @@ class Metronome {
_step() {
const now = new Date();
if (now - this.lastBeat >= this.interval) {
Metronome._dispatch(this.lastBeat, now, this.factor);
this._dispatch(this.lastBeat, now, this.factor);
this.lastBeat = now;
this.factor += this.factor < 9 ? 1 : -9;
}
Expand Down
13 changes: 7 additions & 6 deletions src/utils/metronome.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ chai.use(sinonChai);

describe('Metronome', () => {
let metronome;
const spyDispatch = spy();

beforeEach(() => {
metronome = new Metronome();
metronome = new Metronome(spyDispatch);
});

afterEach(() => {
Expand All @@ -23,6 +24,7 @@ describe('Metronome', () => {
expect(metronome.interval).to.be.equal(SYNC_ACTIVE_INTERVAL);
expect(metronome.running).to.be.equal(false);
expect(metronome.factor).to.be.equal(0);
expect(metronome.dispatchFn).to.be.equal(spyDispatch);
});

describe('init', () => {
Expand Down Expand Up @@ -89,9 +91,8 @@ describe('Metronome', () => {

describe('_dispatch', () => {
it('should dispatch a Vanilla JS event', () => {
const dispatchSpy = spy(document, 'dispatchEvent');
Metronome._dispatch();
expect(dispatchSpy).to.have.been.calledWith();
metronome._dispatch();
expect(spyDispatch).to.have.been.calledWith();
});
});

Expand Down Expand Up @@ -124,14 +125,14 @@ describe('Metronome', () => {
});

it('should call _dispatch if lastBeat is older that 10sec', () => {
const reqSpy = spy(Metronome, '_dispatch');
const reqSpy = spy(metronome, '_dispatch');
metronome.running = true;

const now = new Date();
metronome.lastBeat = now - 20000;
metronome._step();
expect(reqSpy).to.have.been.calledWith();
Metronome._dispatch.restore();
metronome._dispatch.restore();
});
});
});

0 comments on commit 865b414

Please sign in to comment.