From 4260f0f40ae2fddbb3981f36d9f8691d6d5bc897 Mon Sep 17 00:00:00 2001 From: Jarindr Thitadilaka Date: Thu, 12 Jul 2018 00:17:56 +0700 Subject: [PATCH] Add Token test (#324) * add dummy test * actions test * add action test for token * add reducer test * change reducer concept * fix typo --- .../assets/src/omg-mint-token-modal/index.js | 12 +- .../assets/src/omg-token/action.js | 167 +++++++----------- .../assets/src/omg-token/action.test.js | 130 ++++++++++++++ .../assets/src/omg-token/reducer.js | 8 +- .../assets/src/omg-token/reducer.test.js | 75 ++++++++ .../assets/src/services/tokenService.js | 6 +- 6 files changed, 282 insertions(+), 116 deletions(-) create mode 100644 apps/admin_panel/assets/src/omg-token/action.test.js create mode 100644 apps/admin_panel/assets/src/omg-token/reducer.test.js diff --git a/apps/admin_panel/assets/src/omg-mint-token-modal/index.js b/apps/admin_panel/assets/src/omg-mint-token-modal/index.js index 60f6404be..ac23fc35e 100644 --- a/apps/admin_panel/assets/src/omg-mint-token-modal/index.js +++ b/apps/admin_panel/assets/src/omg-mint-token-modal/index.js @@ -8,7 +8,7 @@ import { getWalletsByAccountId } from '../omg-wallet/action' import { connect } from 'react-redux' import { withRouter } from 'react-router-dom' import { compose } from 'recompose' -import {formatAmount} from '../utils/formatter' +import { formatAmount } from '../utils/formatter' const MintTokenModalContainer = styled.form` position: relative; @@ -20,8 +20,8 @@ const MintTokenModalContainer = styled.form` } > i { position: absolute; - right: 0; - top: 0; + right: 15px; + top: 15px; color: ${props => props.theme.colors.S400}; cursor: pointer; } @@ -64,11 +64,11 @@ class MintTokenModal extends PureComponent { id: this.props.token.id, amount: formatAmount(this.state.amount, this.props.token.subunit_to_unit) }) - if (result.data.success) { + if (result.data) { this.props.onRequestClose() - this.setState({ submitStatus: 'SUCCESS', amount: 0 }) + this.setState({ submitStatus: 'SUCCESS', amount: null }) } else { - this.setState({ submitStatus: 'FAILED', amount: 0 }) + this.setState({ submitStatus: 'FAILED' }) } } onRequestClose = e => { diff --git a/apps/admin_panel/assets/src/omg-token/action.js b/apps/admin_panel/assets/src/omg-token/action.js index c98507448..333a0e67c 100644 --- a/apps/admin_panel/assets/src/omg-token/action.js +++ b/apps/admin_panel/assets/src/omg-token/action.js @@ -1,110 +1,63 @@ -import * as tokenSerivce from '../services/tokenService' -export const createToken = ({ name, symbol, decimal, amount }) => async dispatch => { - dispatch({ type: 'TOKEN/CREATE/INITIATED' }) - try { - const result = await tokenSerivce.createToken({ - name, - symbol, - decimal, - amount - }) - if (result.data.success) { - return dispatch({ type: 'TOKEN/CREATE/SUCCESS', data: result.data.data }) - } else { - return dispatch({ type: 'TOKEN/CREATE/FAILED', error: result.data.data }) - } - } catch (error) { - return dispatch({ type: 'TOKEN/CREATE/FAILED', error }) - } -} -export const mintToken = ({ id, amount }) => async dispatch => { - try { - const result = await tokenSerivce.mintToken({ - id, - amount - }) - if (result.data.success) { - dispatch({ type: 'TOKEN/MINT/SUCCESS', data: result.data.data }) - } else { - dispatch({ type: 'TOKEN/MINT/FAILED', error: result.data.data }) - } - return result - } catch (error) { - return dispatch({ type: 'TOKEN/MINT/FAILED', error }) - } -} - -export const getTokens = ({ search, page, perPage, cacheKey }) => async dispatch => { - dispatch({ type: 'TOKENS/REQUEST/INITIATED' }) - try { - const result = await tokenSerivce.getAllTokens({ - perPage: perPage, - page, - sort: { by: 'created_at', dir: 'desc' }, - search - }) - if (result.data.success) { - return dispatch({ - type: 'TOKENS/REQUEST/SUCCESS', - data: result.data.data.data, - pagination: result.data.data.pagination, - cacheKey - }) - } else { - return dispatch({ type: 'TOKENS/REQUEST/FAILED', error: result.data.data }) - } - } catch (error) { - console.log(error) - return dispatch({ type: 'TOKENS/REQUEST/FAILED', error }) - } -} -export const getMintedTokenHistory = ({ - tokenId, - search, - page, - perPage, - searchTerms, - cacheKey -}) => async dispatch => { - dispatch({ type: 'TOKENS/REQUEST/INITIATED' }) - try { - const result = await tokenSerivce.getMintedTokenHistory({ - perPage, - page, - sort: { by: 'created_at', dir: 'desc' }, - search, - searchTerms, - tokenId - }) - if (result.data.success) { - return dispatch({ - type: 'TOKEN_HISTORY/REQUEST/SUCCESS', - data: result.data.data.data, - pagination: result.data.data.pagination, - cacheKey +import * as tokenService from '../services/tokenService' +import { createActionCreator, createPaginationActionCreator } from '../utils/createActionCreator' +export const createToken = ({ name, symbol, decimal, amount }) => + createActionCreator({ + actionName: 'TOKEN', + action: 'CREATE', + service: () => + tokenService.createToken({ + name, + symbol, + decimal, + amount }) - } else { - return dispatch({ type: 'TOKEN_HISTORY/REQUEST/FAILED', error: result.data.data }) - } - } catch (error) { - console.log(error) - return dispatch({ type: 'TOKEN_HISTORY/REQUEST/FAILED', error }) - } -} + }) -export const getTokenById = id => async dispatch => { - try { - const result = await tokenSerivce.getTokenStatsById(id) - if (result.data.success) { - dispatch({ - type: 'TOKEN/REQUEST/SUCCESS', - data: { ...result.data.data.token, total_supply: result.data.data.total_supply } +export const mintToken = ({ id, amount }) => + createActionCreator({ + actionName: 'TOKEN', + action: 'MINT', + service: () => + tokenService.mintToken({ + id, + amount }) - } else { - dispatch({ type: 'TOKEN/REQUEST/FAILED', error: result.data.data }) - } - return result - } catch (error) { - return dispatch({ type: 'TOKEN/REQUEST/FAILED', error }) - } -} + }) + +export const getTokens = ({ search, page, perPage, cacheKey, searchTerms }) => + createPaginationActionCreator({ + actionName: 'TOKENS', + action: 'REQUEST', + service: () => + tokenService.getAllTokens({ + perPage, + page, + searchTerms, + sort: { by: 'created_at', dir: 'desc' }, + search + }), + cacheKey + }) + +export const getMintedTokenHistory = ({ tokenId, search, page, perPage, searchTerms, cacheKey }) => + createPaginationActionCreator({ + actionName: 'TOKEN_HISTORY', + action: 'REQUEST', + service: () => + tokenService.getMintedTokenHistory({ + perPage, + page, + sort: { by: 'created_at', dir: 'desc' }, + search, + searchTerms, + tokenId + }), + cacheKey + }) + +export const getTokenById = id => + createActionCreator({ + actionName: 'TOKEN', + action: 'REQUEST', + service: () => tokenService.getTokenStatsById(id) + }) diff --git a/apps/admin_panel/assets/src/omg-token/action.test.js b/apps/admin_panel/assets/src/omg-token/action.test.js new file mode 100644 index 000000000..b9a45a7bc --- /dev/null +++ b/apps/admin_panel/assets/src/omg-token/action.test.js @@ -0,0 +1,130 @@ +import configureMockStore from 'redux-mock-store' +import thunk from 'redux-thunk' +import { createToken, mintToken, getMintedTokenHistory, getTokenById, getTokens } from './action' +import * as tokenService from '../services/tokenService' +const middlewares = [thunk] +const mockStore = configureMockStore(middlewares) +jest.mock('../services/tokenService') +let store +describe('token actions', () => { + beforeEach(() => { + jest.resetAllMocks() + store = mockStore() + }) + test('[createToken] should dispatch success action if successfully create token', () => { + tokenService.createToken.mockImplementation(() => { + return Promise.resolve({ data: { success: true, data: 'data' } }) + }) + const expectedActions = [ + { type: 'TOKEN/CREATE/INITIATED' }, + { type: 'TOKEN/CREATE/SUCCESS', data: 'data' } + ] + return store + .dispatch(createToken({ name: 'name', symbol: 'symbol', decimal: 'decimal', amount: '50' })) + .then(() => { + expect(tokenService.createToken).toBeCalledWith({ + name: 'name', + symbol: 'symbol', + decimal: 'decimal', + amount: '50' + }) + expect(store.getActions()).toEqual(expectedActions) + }) + }) + + test('[mintToken] should dispatch success action if successfully mintToken', () => { + tokenService.mintToken.mockImplementation(() => { + return Promise.resolve({ data: { success: true, data: 'data' } }) + }) + const expectedActions = [ + { type: 'TOKEN/MINT/INITIATED' }, + { type: 'TOKEN/MINT/SUCCESS', data: 'data' } + ] + return store.dispatch(mintToken({ id: 'id', amount: '50' })).then(() => { + expect(tokenService.mintToken).toBeCalledWith({ id: 'id', amount: '50' }) + expect(store.getActions()).toEqual(expectedActions) + }) + }) + + test('[getMintedTokenHistory] should dispatch success action if successfully getMintedTokenHistory', () => { + tokenService.getMintedTokenHistory.mockImplementation(() => { + return Promise.resolve({ + data: { + success: true, + data: { data: 'data', pagination: 'pagination' } + } + }) + }) + const expectedActions = [ + { type: 'TOKEN_HISTORY/REQUEST/INITIATED' }, + { + type: 'TOKEN_HISTORY/REQUEST/SUCCESS', + data: 'data', + pagination: 'pagination', + cacheKey: 'key' + } + ] + return store + .dispatch(getMintedTokenHistory({ page: 1, perPage: 10, cacheKey: 'key', search: 'search' })) + .then(() => { + expect(tokenService.getMintedTokenHistory).toBeCalledWith( + expect.objectContaining({ + page: 1, + perPage: 10, + sort: { by: 'created_at', dir: 'desc' }, + search: 'search' + }) + ) + expect(store.getActions()).toEqual(expectedActions) + }) + }) + + test('[getTokens] should dispatch success action if successfully getTokens', () => { + tokenService.getAllTokens.mockImplementation(() => { + return Promise.resolve({ + data: { + success: true, + data: { data: 'data', pagination: 'pagination' } + } + }) + }) + const expectedActions = [ + { type: 'TOKENS/REQUEST/INITIATED' }, + { + type: 'TOKENS/REQUEST/SUCCESS', + data: 'data', + pagination: 'pagination', + cacheKey: 'key' + } + ] + return store + .dispatch(getTokens({ page: 1, perPage: 10, cacheKey: 'key', search: 'search' })) + .then(() => { + expect(tokenService.getAllTokens).toBeCalledWith( + expect.objectContaining({ + page: 1, + perPage: 10, + sort: { by: 'created_at', dir: 'desc' }, + search: 'search' + }) + ) + expect(store.getActions()).toEqual(expectedActions) + }) + }) + + test('[getTokenById] should dispatch success action if successfully getTokenById', () => { + tokenService.getTokenStatsById.mockImplementation(() => { + return Promise.resolve({ + data: { success: true, data: { token: { id: '1' }, total_supply: 1 } } + }) + }) + const expectedActions = [ + { type: 'TOKEN/REQUEST/INITIATED' }, + { type: 'TOKEN/REQUEST/SUCCESS', data: { token: { id: '1' }, total_supply: 1 } } + ] + return store.dispatch(getTokenById('id')).then(() => { + expect(tokenService.getTokenStatsById).toBeCalledWith('id') + expect(store.getActions()).toEqual(expectedActions) + }) + }) +}) diff --git a/apps/admin_panel/assets/src/omg-token/reducer.js b/apps/admin_panel/assets/src/omg-token/reducer.js index fe9c242f1..365988b36 100644 --- a/apps/admin_panel/assets/src/omg-token/reducer.js +++ b/apps/admin_panel/assets/src/omg-token/reducer.js @@ -1,4 +1,5 @@ import createReducer from '../reducer/createReducer' +import _ from 'lodash' export const tokensReducer = createReducer( {}, { @@ -6,7 +7,12 @@ export const tokensReducer = createReducer( return _.merge(state, _.keyBy(data, 'id')) }, 'TOKEN/REQUEST/SUCCESS': (state, { data }) => { - return { ...state, [data.id]: data } + return _.merge(state, { + [data.token.id]: { + ...data.token, + total_supply: data.total_supply + } + }) }, 'TOKEN/CREATE/SUCCESS': (state, { data }) => { return { ...state, [data.id]: data } diff --git a/apps/admin_panel/assets/src/omg-token/reducer.test.js b/apps/admin_panel/assets/src/omg-token/reducer.test.js new file mode 100644 index 000000000..feb9ab1fc --- /dev/null +++ b/apps/admin_panel/assets/src/omg-token/reducer.test.js @@ -0,0 +1,75 @@ +import { tokensReducer, mintedTokenHistoryReducer } from './reducer' + +describe('tokens reducer', () => { + test('[tokensReducer] should return the initial state', () => { + expect(tokensReducer({}, 'FAKE_ACTION')).toEqual({}) + }) + test('[mintedTokenHistoryReducer] should return the initial state', () => { + expect(mintedTokenHistoryReducer({}, 'FAKE_ACTION')).toEqual({}) + }) + test('[tokensReducer] should return the key by id object with action [TOKENS/REQUEST/SUCCESS]', () => { + expect( + tokensReducer( + {}, + { + type: 'TOKENS/REQUEST/SUCCESS', + data: [{ id: '1', data: '1' }] + } + ) + ).toEqual({ + '1': { + id: '1', + data: '1' + } + }) + }) + test('[tokensReducer] should merge and keep total supply key', () => { + expect( + tokensReducer( + { '1': { id: '1', data: '1', total_supply: '1' } }, + { + type: 'TOKENS/REQUEST/SUCCESS', + data: [{ id: '1', data: '1' }] + } + ) + ).toEqual({ + '1': { + id: '1', + data: '1', + total_supply: '1' + } + }) + }) + test('[mintedTokenHistoryReducer] should return the key by id object with action [TOKEN_HISTORY/REQUEST/SUCCESS]', () => { + expect( + mintedTokenHistoryReducer( + {}, + { + type: 'TOKEN_HISTORY/REQUEST/SUCCESS', + data: [{ id: '1', data: '1' }] + } + ) + ).toEqual({ + '1': { + id: '1', + data: '1' + } + }) + }) + test('[tokensReducer] should inject total_supply and increse it with action [TOKEN/MINT/SUCCESS]', () => { + expect( + tokensReducer( + { '1': { total_supply: 1 } }, + { + type: 'TOKEN/MINT/SUCCESS', + data: { token: { id: '1' }, amount: 1 } + } + ) + ).toEqual({ + '1': { + total_supply: 2, + id: '1' + } + }) + }) +}) diff --git a/apps/admin_panel/assets/src/services/tokenService.js b/apps/admin_panel/assets/src/services/tokenService.js index 6b0685ba1..bb9086e65 100644 --- a/apps/admin_panel/assets/src/services/tokenService.js +++ b/apps/admin_panel/assets/src/services/tokenService.js @@ -1,13 +1,15 @@ import { authenticatedRequest } from './apiService' -export function getAllTokens ({ perPage, sort, search, ...rest }) { +export function getAllTokens ({ page, perPage, sort, search, searchTerms }) { return authenticatedRequest({ path: '/token.all', data: { + page, per_page: perPage, sort_by: sort.by, sort_dir: sort.dir, - search_term: search + search_term: search, + search_terms: searchTerms } }) }