From c31490c8b4e12b459e034126c8154273ef17c28a Mon Sep 17 00:00:00 2001 From: skubakdj Date: Fri, 17 Nov 2017 16:12:27 -0500 Subject: [PATCH] Redux Reducer Tests (#390) * set return type in resetWallet action creator * update config reducer test * update generateWallet reducer test * update swap reducer test * update wallet reducer test * create customTokens reducer test * create deterministicWallets reducer test * create ens reducer test * create notifications reducer test * add crypto compare success/fail actions * add rates reducer test * remove unnecessary comments * remove more comments * remove duplicate import * update wallet reducer test to use BN * update dWallet reducer test to use BN * update wallet reducer tests * update rates reducer tests --- common/actions/rates/actionCreators.ts | 19 ++++- common/actions/wallet/actionCreators.ts | 2 +- spec/reducers/config.spec.ts | 48 +++++++++++ spec/reducers/customTokens.spec.ts | 37 +++++++++ spec/reducers/deterministicWallets.spec.ts | 81 ++++++++++++++++++ spec/reducers/ens.spec.ts | 15 ++++ spec/reducers/generateWallet.spec.ts | 12 +++ spec/reducers/notifications.spec.ts | 28 +++++++ spec/reducers/rates.spec.ts | 33 ++++++++ spec/reducers/swap.spec.ts | 83 +++++++++++++++--- spec/reducers/wallet.spec.ts | 97 +++++++++++++++++++++- 11 files changed, 439 insertions(+), 16 deletions(-) create mode 100644 spec/reducers/customTokens.spec.ts create mode 100644 spec/reducers/deterministicWallets.spec.ts create mode 100644 spec/reducers/ens.spec.ts create mode 100644 spec/reducers/notifications.spec.ts create mode 100644 spec/reducers/rates.spec.ts diff --git a/common/actions/rates/actionCreators.ts b/common/actions/rates/actionCreators.ts index 3730d44c..2fc5255b 100644 --- a/common/actions/rates/actionCreators.ts +++ b/common/actions/rates/actionCreators.ts @@ -1,6 +1,6 @@ import * as interfaces from './actionTypes'; import { TypeKeys } from './constants'; -import { fetchRates } from './actionPayloads'; +import { fetchRates, CCResponse } from './actionPayloads'; export type TFetchCCRates = typeof fetchCCRates; export function fetchCCRates(symbol: string): interfaces.FetchCCRates { @@ -9,3 +9,20 @@ export function fetchCCRates(symbol: string): interfaces.FetchCCRates { payload: fetchRates(symbol) }; } + +export type TFetchCCRatesSucceeded = typeof fetchCCRatesSucceeded; +export function fetchCCRatesSucceeded( + payload: CCResponse +): interfaces.FetchCCRatesSucceeded { + return { + type: TypeKeys.RATES_FETCH_CC_SUCCEEDED, + payload + }; +} + +export type TFetchCCRatesFailed = typeof fetchCCRatesFailed; +export function fetchCCRatesFailed(): interfaces.FetchCCRatesFailed { + return { + type: TypeKeys.RATES_FETCH_CC_FAILED + }; +} diff --git a/common/actions/wallet/actionCreators.ts b/common/actions/wallet/actionCreators.ts index e078772e..8acf32dc 100644 --- a/common/actions/wallet/actionCreators.ts +++ b/common/actions/wallet/actionCreators.ts @@ -120,7 +120,7 @@ export function broadCastTxFailed( } export type TResetWallet = typeof resetWallet; -export function resetWallet() { +export function resetWallet(): types.ResetWalletAction { return { type: TypeKeys.WALLET_RESET }; diff --git a/spec/reducers/config.spec.ts b/spec/reducers/config.spec.ts index 71587cc5..ee6d930b 100644 --- a/spec/reducers/config.spec.ts +++ b/spec/reducers/config.spec.ts @@ -28,4 +28,52 @@ describe('config reducer', () => { gasPriceGwei: gasPrice }); }); + + it('should handle CONFIG_TOGGLE_OFFLINE', () => { + const offlineState = { + ...INITIAL_STATE, + offline: true + }; + + const onlineState = { + ...INITIAL_STATE, + offline: false + }; + + expect(config(offlineState, configActions.toggleOfflineConfig())).toEqual({ + ...offlineState, + offline: false + }); + + expect(config(onlineState, configActions.toggleOfflineConfig())).toEqual({ + ...onlineState, + offline: true + }); + }); + + it('should handle CONFIG_FORCE_OFFLINE', () => { + const forceOfflineTrue = { + ...INITIAL_STATE, + forceOffline: true + }; + + const forceOfflineFalse = { + ...INITIAL_STATE, + forceOffline: false + }; + + expect( + config(forceOfflineTrue, configActions.forceOfflineConfig()) + ).toEqual({ + ...forceOfflineTrue, + forceOffline: false + }); + + expect( + config(forceOfflineFalse, configActions.forceOfflineConfig()) + ).toEqual({ + ...forceOfflineFalse, + forceOffline: true + }); + }); }); diff --git a/spec/reducers/customTokens.spec.ts b/spec/reducers/customTokens.spec.ts new file mode 100644 index 00000000..02e08743 --- /dev/null +++ b/spec/reducers/customTokens.spec.ts @@ -0,0 +1,37 @@ +import { customTokens } from 'reducers/customTokens'; +import { Token } from 'config/data'; +import * as customTokensActions from 'actions/customTokens'; + +describe('customTokens reducer', () => { + const token1: Token = { + address: 'address', + symbol: 'OMG', + decimal: 16 + }; + const token2: Token = { + address: 'address', + symbol: 'ANT', + decimal: 16 + }; + + it('should handle CUSTOM_TOKEN_ADD', () => { + expect( + customTokens(undefined, customTokensActions.addCustomToken(token1)) + ).toEqual([token1]); + }); + + it('should handle CUSTOM_TOKEN_REMOVE', () => { + const state1 = customTokens( + undefined, + customTokensActions.addCustomToken(token1) + ); + const state2 = customTokens( + state1, + customTokensActions.addCustomToken(token2) + ); + + expect( + customTokens(state2, customTokensActions.removeCustomToken(token2.symbol)) + ).toEqual([token1]); + }); +}); diff --git a/spec/reducers/deterministicWallets.spec.ts b/spec/reducers/deterministicWallets.spec.ts new file mode 100644 index 00000000..90ebbffe --- /dev/null +++ b/spec/reducers/deterministicWallets.spec.ts @@ -0,0 +1,81 @@ +import { + deterministicWallets, + INITIAL_STATE +} from 'reducers/deterministicWallets'; +import * as dWalletActions from 'actions/deterministicWallets'; +import { TokenValue } from 'libs/units'; + +describe('deterministicWallets reducer', () => { + const tokenValues: dWalletActions.ITokenValues = { + OMG: { + value: TokenValue('0'), + decimal: 16 + } + }; + + const wallet: dWalletActions.DeterministicWalletData = { + index: 0, + address: 'address', + value: TokenValue('0'), + tokenValues + }; + + it('should handle DW_SET_WALLETS', () => { + const wallets = [wallet]; + expect( + deterministicWallets( + undefined, + dWalletActions.setDeterministicWallets(wallets) + ) + ).toEqual({ + ...INITIAL_STATE, + wallets + }); + }); + + it('should handle DW_SET_DESIRED_TOKEN', () => { + const desiredToken = 'OMG'; + expect( + deterministicWallets( + undefined, + dWalletActions.setDesiredToken(desiredToken) + ) + ).toEqual({ + ...INITIAL_STATE, + desiredToken + }); + }); + + it('should handle DW_UPDATE_WALLET', () => { + const wallet1 = { + ...wallet, + address: 'wallet1' + }; + const wallet2 = { + ...wallet, + address: 'wallet2' + }; + const wallets = [wallet1, wallet2]; + const state = deterministicWallets( + undefined, + dWalletActions.setDeterministicWallets(wallets) + ); + + const wallet2Update = { + ...wallet, + index: 100, + address: 'wallet2', + value: TokenValue('100') + }; + + expect( + deterministicWallets( + state, + dWalletActions.updateDeterministicWallet(wallet2Update) + ) + ).toEqual({ + ...INITIAL_STATE, + wallets: [wallet1, wallet2Update] + }); + }); +}); diff --git a/spec/reducers/ens.spec.ts b/spec/reducers/ens.spec.ts new file mode 100644 index 00000000..640d2833 --- /dev/null +++ b/spec/reducers/ens.spec.ts @@ -0,0 +1,15 @@ +import { ens, INITIAL_STATE } from 'reducers/ens'; +import * as ensActions from 'actions/ens'; + +describe('customTokens reducer', () => { + it('should handle ENS_CACHE', () => { + const ensName = 'ensName'; + const address = 'address'; + expect( + ens(undefined, ensActions.cacheEnsAddress(ensName, address)) + ).toEqual({ + ...INITIAL_STATE, + [ensName]: address + }); + }); +}); diff --git a/spec/reducers/generateWallet.spec.ts b/spec/reducers/generateWallet.spec.ts index 66c639a6..b8746cd0 100644 --- a/spec/reducers/generateWallet.spec.ts +++ b/spec/reducers/generateWallet.spec.ts @@ -1,7 +1,19 @@ import { generateWallet, INITIAL_STATE } from 'reducers/generateWallet'; import * as generateWalletActions from 'actions/generateWallet'; +import Wallet from 'ethereumjs-wallet'; describe('generateWallet reducer', () => { + it('should handle GENERATE_WALLET_GENERATE_WALLET', () => { + const { wallet, password, activeStep } = generateWallet( + undefined, + generateWalletActions.generateNewWallet('password') + ); + + expect(wallet).toBeInstanceOf(Wallet); + expect(password).toEqual('password'); + expect(activeStep).toEqual('download'); + }); + it('should handle GENERATE_WALLET_CONTINUE_TO_PAPER', () => { expect( generateWallet(undefined, generateWalletActions.continueToPaper()) diff --git a/spec/reducers/notifications.spec.ts b/spec/reducers/notifications.spec.ts new file mode 100644 index 00000000..c7d3e96e --- /dev/null +++ b/spec/reducers/notifications.spec.ts @@ -0,0 +1,28 @@ +import { notifications } from 'reducers/notifications'; +import * as notificationsActions from 'actions/notifications'; + +describe('customTokens reducer', () => { + const notification1 = notificationsActions.showNotification('success', 'msg'); + + const notification2 = notificationsActions.showNotification('danger', 'msg'); + + it('should handle SHOW_NOTIFICATION', () => { + const state = notifications(undefined, notification1); + expect(notifications(state, notification2)).toEqual([ + notification1.payload, + notification2.payload + ]); + }); + + it('should handle CLOSE_NOTIFICATION', () => { + const state1 = notifications(undefined, notification1); + const state2 = notifications(state1, notification2); + + expect( + notifications( + state2, + notificationsActions.closeNotification(notification2.payload) + ) + ).toEqual([notification1.payload]); + }); +}); diff --git a/spec/reducers/rates.spec.ts b/spec/reducers/rates.spec.ts new file mode 100644 index 00000000..902891f7 --- /dev/null +++ b/spec/reducers/rates.spec.ts @@ -0,0 +1,33 @@ +import { rates, INITIAL_STATE } from 'reducers/rates'; +import * as ratesActions from 'actions/rates'; + +describe('rates reducer', () => { + it('should handle RATES_FETCH_CC_SUCCEEDED', () => { + const fakeCCResp: ratesActions.CCResponse = { + symbol: 'USD', + rates: { + BTC: 1, + EUR: 2, + GBP: 3, + CHF: 4, + REP: 5, + ETH: 6 + } + }; + expect( + rates(undefined, ratesActions.fetchCCRatesSucceeded(fakeCCResp)) + ).toEqual({ + ...INITIAL_STATE, + rates: { + ...INITIAL_STATE.rates, + [fakeCCResp.symbol]: fakeCCResp.rates + } + }); + }); + + it('should handle RATES_FETCH_CC_FAILED', () => { + expect(rates(undefined, ratesActions.fetchCCRatesFailed())).toHaveProperty( + 'ratesError' + ); + }); +}); diff --git a/spec/reducers/swap.spec.ts b/spec/reducers/swap.spec.ts index 06a09acb..7c4dafff 100644 --- a/spec/reducers/swap.spec.ts +++ b/spec/reducers/swap.spec.ts @@ -59,12 +59,12 @@ describe('swap reducer', () => { it('should handle SWAP_ORIGIN_AMOUNT', () => { const originAmount = 2; - expect( - swap(undefined, swapActions.originAmountSwap(originAmount)) - ).toEqual({ - ...INITIAL_STATE, - originAmount - }); + expect(swap(undefined, swapActions.originAmountSwap(originAmount))).toEqual( + { + ...INITIAL_STATE, + originAmount + } + ); }); it('should handle SWAP_DESTINATION_AMOUNT', () => { @@ -153,12 +153,71 @@ describe('swap reducer', () => { }); }); - // TODO - // it('should handle SWAP_BITY_ORDER_CREATE_SUCCEEDED', () => { - // }); - // - // it('should handle SWAP_BITY_ORDER_STATUS_SUCCEEDED', () => { - // }); + it('should handle SWAP_BITY_ORDER_CREATE_SUCCEEDED', () => { + const mockedBityOrder: swapActions.BityOrderPostResponse = { + payment_address: 'payment_address', + status: 'status', + input: { + amount: '1.111', + currency: 'input_currency', + reference: 'input_reference', + status: 'input_status' + }, + output: { + amount: '1.111', + currency: 'output_currency', + reference: 'output_reference', + status: 'output_status' + }, + timestamp_created: 'timestamp_created', + validFor: 0, + id: 'id' + }; + + expect( + swap(undefined, swapActions.bityOrderCreateSucceededSwap(mockedBityOrder)) + ).toEqual({ + ...INITIAL_STATE, + bityOrder: { + ...mockedBityOrder + }, + isPostingOrder: false, + originAmount: parseFloat(mockedBityOrder.input.amount), + destinationAmount: parseFloat(mockedBityOrder.output.amount), + secondsRemaining: mockedBityOrder.validFor, + validFor: mockedBityOrder.validFor, + orderTimestampCreatedISOString: mockedBityOrder.timestamp_created, + paymentAddress: mockedBityOrder.payment_address, + orderStatus: mockedBityOrder.status, + orderId: mockedBityOrder.id + }); + }); + + it('should handle SWAP_BITY_ORDER_STATUS_SUCCEEDED', () => { + const mockedBityResponse: swapActions.BityOrderResponse = { + input: { + amount: '1.111', + currency: 'input_currency', + reference: 'input_reference', + status: 'input_status' + }, + output: { + amount: '1.111', + currency: 'output_currency', + reference: 'output_reference', + status: 'FILL' + }, + status: 'status' + }; + + expect( + swap(undefined, swapActions.orderStatusSucceededSwap(mockedBityResponse)) + ).toEqual({ + ...INITIAL_STATE, + outputTx: mockedBityResponse.output.reference, + orderStatus: mockedBityResponse.output.status + }); + }); it('should handle SWAP_ORDER_TIME', () => { const secondsRemaining = 300; diff --git a/spec/reducers/wallet.spec.ts b/spec/reducers/wallet.spec.ts index 4b395bf5..a12b039a 100644 --- a/spec/reducers/wallet.spec.ts +++ b/spec/reducers/wallet.spec.ts @@ -1,11 +1,11 @@ import { wallet, INITIAL_STATE } from 'reducers/wallet'; +import { Wei, TokenValue } from 'libs/units'; import * as walletActions from 'actions/wallet'; -import { TokenValue } from 'libs/units'; describe('wallet reducer', () => { it('should handle WALLET_SET', () => { const doSomething = new Promise(resolve => { - setTimeout(() => resolve('Success'), 1000); + setTimeout(() => resolve('Success'), 10); }); const walletInstance = { @@ -20,6 +20,45 @@ describe('wallet reducer', () => { }); }); + it('should handle WALLET_RESET', () => { + expect(wallet(undefined, walletActions.resetWallet())).toEqual( + INITIAL_STATE + ); + }); + + it('should handle WALLET_SET_BALANCE_PENDING', () => { + expect(wallet(undefined, walletActions.setBalancePending())).toEqual({ + ...INITIAL_STATE, + balance: { + ...INITIAL_STATE.balance, + isPending: true + } + }); + }); + + it('should handle WALLET_SET_BALANCE_FULFILLED', () => { + const balance = Wei('100'); + expect( + wallet(undefined, walletActions.setBalanceFullfilled(balance)) + ).toEqual({ + ...INITIAL_STATE, + balance: { + wei: balance, + isPending: false + } + }); + }); + + it('should handle WALLET_SET_BALANCE_REJECTED', () => { + expect(wallet(undefined, walletActions.setBalanceRejected())).toEqual({ + ...INITIAL_STATE, + balance: { + ...INITIAL_STATE.balance, + isPending: false + } + }); + }); + it('should handle WALLET_SET_TOKEN_BALANCES', () => { const tokenBalances = { OMG: TokenValue('20') }; expect( @@ -29,4 +68,58 @@ describe('wallet reducer', () => { tokens: tokenBalances }); }); + + it('should handle WALLET_BROADCAST_TX_REQUESTED', () => { + const signedTx = '0xdeadbeef'; + + // test broadcast where first time seeing transaction + expect(wallet(undefined, walletActions.broadcastTx(signedTx))).toEqual({ + ...INITIAL_STATE, + transactions: [ + { + signedTx, + isBroadcasting: true, + successfullyBroadcast: false + } + ] + }); + }); + + it('should handle WALLET_BROADCAST_TX_SUCCEEDED', () => { + const signedTx = '0xdead'; + const txHash = '0xbeef'; + const state = wallet(undefined, walletActions.broadcastTx(signedTx)); + + expect( + wallet(state, walletActions.broadcastTxSucceded(txHash, signedTx)) + ).toEqual({ + ...INITIAL_STATE, + transactions: [ + { + signedTx, + isBroadcasting: false, + successfullyBroadcast: true + } + ] + }); + }); + + it('should handle WALLET_BROADCAST_TX_FAILED', () => { + const signedTx = '0xdeadbeef'; + const errorMsg = 'Broadcasting failed.'; + const state = wallet(undefined, walletActions.broadcastTx(signedTx)); + + expect( + wallet(state, walletActions.broadCastTxFailed(signedTx, errorMsg)) + ).toEqual({ + ...INITIAL_STATE, + transactions: [ + { + signedTx, + isBroadcasting: false, + successfullyBroadcast: false + } + ] + }); + }); });