Add reducer tests for new redux state

This commit is contained in:
HenryNguyen5 2018-02-05 12:46:52 -05:00
parent 8048157448
commit dccd976043
17 changed files with 525 additions and 124 deletions

View File

@ -1,8 +1,8 @@
import * as interfaces from './actionTypes'; import * as interfaces from './actionTypes';
import { TypeKeys } from './constants'; import { TypeKeys } from './constants';
export type TToggleOfflineConfig = typeof toggleOfflineConfig; export type TToggleOffline = typeof toggleOffline;
export function toggleOfflineConfig(): interfaces.ToggleOfflineAction { export function toggleOffline(): interfaces.ToggleOfflineAction {
return { return {
type: TypeKeys.CONFIG_TOGGLE_OFFLINE type: TypeKeys.CONFIG_TOGGLE_OFFLINE
}; };
@ -96,7 +96,9 @@ export function setLatestBlock(payload: string): interfaces.SetLatestBlockAction
}; };
} }
export function web3SetNode(payload: interfaces.Web3setNodeAction['payload']) { export function web3SetNode(
payload: interfaces.Web3setNodeAction['payload']
): interfaces.Web3setNodeAction {
return { return {
type: TypeKeys.CONFIG_NODE_WEB3_SET, type: TypeKeys.CONFIG_NODE_WEB3_SET,
payload payload

View File

@ -24,7 +24,9 @@ export function sanitizeHex(hex: string) {
return hex !== '' ? `0x${padLeftEven(hexStr)}` : ''; return hex !== '' ? `0x${padLeftEven(hexStr)}` : '';
} }
export function networkIdToName(networkId: string | number): StaticNetworkIds { export function networkIdToName(
networkId: 1 | 3 | 4 | 42 | '1' | '3' | '4' | '42'
): StaticNetworkIds {
switch (networkId.toString()) { switch (networkId.toString()) {
case '1': case '1':
return 'ETH'; return 'ETH';

View File

@ -2,7 +2,6 @@ import { ChangeLanguageAction, SetLatestBlockAction, MetaAction } from 'actions/
import { TypeKeys } from 'actions/config/constants'; import { TypeKeys } from 'actions/config/constants';
export interface State { export interface State {
// FIXME
languageSelection: string; languageSelection: string;
offline: boolean; offline: boolean;
autoGasLimit: boolean; autoGasLimit: boolean;

View File

@ -1,5 +1,5 @@
import { NodeAction, TypeKeys, ChangeNodeAction } from 'actions/config'; import { NodeAction, TypeKeys, ChangeNodeAction } from 'actions/config';
import { INITIAL_STATE as INITIAL_NODE_STATE } from '../nodes/selectedNode'; // could probably consolidate this in the index file of 'nodes' to make it easier to import import { INITIAL_STATE as INITIAL_NODE_STATE } from '../nodes/selectedNode';
import { INITIAL_STATE as INITIAL_DEFAULT_NODE_STATE } from '../nodes/staticNodes'; import { INITIAL_STATE as INITIAL_DEFAULT_NODE_STATE } from '../nodes/staticNodes';
import { NonWeb3NodeConfigs } from 'types/node'; import { NonWeb3NodeConfigs } from 'types/node';
import { StaticNetworkIds } from 'types/network'; import { StaticNetworkIds } from 'types/network';

View File

@ -17,7 +17,7 @@ export type State = { [key in StaticNetworkIds]: StaticNetworkConfig };
// Must be a website that follows the ethplorer convention of /tx/[hash] and // Must be a website that follows the ethplorer convention of /tx/[hash] and
// address/[address] to generate the correct functions. // address/[address] to generate the correct functions.
// TODO: put this in utils / libs // TODO: put this in utils / libs
function makeExplorer(origin: string): BlockExplorerConfig { export function makeExplorer(origin: string): BlockExplorerConfig {
return { return {
origin, origin,
txUrl: hash => `${origin}/tx/${hash}`, txUrl: hash => `${origin}/tx/${hash}`,

View File

@ -1,5 +1,5 @@
import { EtherscanNode, InfuraNode, RPCNode } from 'libs/nodes'; import { EtherscanNode, InfuraNode, RPCNode } from 'libs/nodes';
import { ConfigAction, TypeKeys } from 'actions/config'; import { TypeKeys, NodeAction } from 'actions/config';
import { NonWeb3NodeConfigs, Web3NodeConfigs } from 'types/node'; import { NonWeb3NodeConfigs, Web3NodeConfigs } from 'types/node';
export type State = NonWeb3NodeConfigs & Web3NodeConfigs; export type State = NonWeb3NodeConfigs & Web3NodeConfigs;
@ -91,7 +91,7 @@ export const INITIAL_STATE: State = {
} }
}; };
export const staticNodes = (state: State = INITIAL_STATE, action: ConfigAction) => { export const staticNodes = (state: State = INITIAL_STATE, action: NodeAction) => {
switch (action.type) { switch (action.type) {
case TypeKeys.CONFIG_NODE_WEB3_SET: case TypeKeys.CONFIG_NODE_WEB3_SET:
return { ...state, [action.payload.id]: action.payload.config }; return { ...state, [action.payload.id]: action.payload.config };

View File

@ -22,7 +22,7 @@ import {
} from 'selectors/config'; } from 'selectors/config';
import { TypeKeys } from 'actions/config/constants'; import { TypeKeys } from 'actions/config/constants';
import { import {
toggleOfflineConfig, toggleOffline,
changeNode, changeNode,
changeNodeIntent, changeNodeIntent,
setLatestBlock, setLatestBlock,
@ -55,7 +55,7 @@ export function* pollOfflineStatus(): SagaIterator {
yield put( yield put(
showNotification('success', 'Your connection to the network has been restored!', 3000) showNotification('success', 'Your connection to the network has been restored!', 3000)
); );
yield put(toggleOfflineConfig()); yield put(toggleOffline());
} else if (!pingSucceeded && !isOffline) { } else if (!pingSucceeded && !isOffline) {
// If we were unable to ping but redux says we're online, mark offline // If we were unable to ping but redux says we're online, mark offline
// If they had been online, show an error. // If they had been online, show an error.
@ -79,7 +79,7 @@ export function* pollOfflineStatus(): SagaIterator {
) )
); );
} }
yield put(toggleOfflineConfig()); yield put(toggleOffline());
} else { } else {
// If neither case was true, try again in 5s // If neither case was true, try again in 5s
yield call(delay, 5000); yield call(delay, 5000);

View File

@ -1,19 +0,0 @@
import { StaticNetworkConfig } from 'types/network';
describe('Networks', () => {
Object.keys(NETWORKS).forEach(networkId => {
it(`${networkId} contains non-null dPathFormats`, () => {
const network: StaticNetworkConfig = NETWORKS[networkId];
Object.values(network.dPathFormats).forEach(dPathFormat => {
expect(dPathFormat).toBeTruthy();
});
});
});
it(`contain unique chainIds`, () => {
const networkValues = Object.values(NETWORKS);
const chainIds = networkValues.map(a => a.chainId);
const chainIdsSet = new Set(chainIds);
expect(Array.from(chainIdsSet).length).toEqual(chainIds.length);
});
});

View File

@ -1,90 +0,0 @@
import { config } from 'reducers/config';
import * as configActions from 'actions/config';
import { makeCustomNodeId, makeNodeConfigFromCustomConfig } from 'utils/node';
const custNode = {
name: 'Test Config',
url: 'http://somecustomconfig.org/',
port: 443,
network: 'ETH'
};
describe('config reducer', () => {
it('should handle CONFIG_LANGUAGE_CHANGE', () => {
const language = 'en';
expect(config(undefined, configActions.changeLanguage(language))).toEqual({
...INITIAL_STATE,
languageSelection: language
});
});
it('should handle CONFIG_NODE_CHANGE', () => {
const key = Object.keys(NODES)[0];
const node = NODES[key];
const network = NETWORKS[node.network];
expect(config(undefined, configActions.changeNode(key, node, network))).toEqual({
...INITIAL_STATE,
node: NODES[key],
nodeSelection: key
});
});
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_ADD_CUSTOM_NODE', () => {
expect(config(undefined, configActions.addCustomNode(custNode))).toEqual({
...INITIAL_STATE,
customNodes: [custNode]
});
});
describe('should handle CONFIG_REMOVE_CUSTOM_NODE', () => {
const customNodeId = makeCustomNodeId(custNode);
const addedState = config(undefined, configActions.addCustomNode(custNode));
const addedAndActiveState = config(
addedState,
configActions.changeNode(
customNodeId,
makeNodeConfigFromCustomConfig(custNode),
NETWORKS[custNode.network]
)
);
const removedState = config(addedAndActiveState, configActions.removeCustomNode(custNode));
it('should remove the custom node from `customNodes`', () => {
expect(removedState.customNodes.length).toBe(0);
});
it('should change the active node, if the custom one was active', () => {
expect(removedState.nodeSelection === customNodeId).toBeFalsy();
});
});
it('should handle CONFIG_SET_LATEST_BLOCK', () => {
expect(config(undefined, configActions.setLatestBlock('12345'))).toEqual({
...INITIAL_STATE,
latestBlock: '12345'
});
});
});

View File

@ -0,0 +1,74 @@
import { meta } from 'reducers/config/meta';
import { changeLanguage, toggleOffline, toggleAutoGasLimit, setLatestBlock } from 'actions/config';
const expectedInitialState = {
languageSelection: 'en',
offline: false,
autoGasLimit: true,
latestBlock: '???'
};
const expectedState = {
initialState: expectedInitialState,
changingLanguage: {
...expectedInitialState,
languageSelection: 'langaugeToChange'
},
togglingToOffline: {
...expectedInitialState,
offline: true
},
togglingToOnline: {
...expectedInitialState,
offline: false
},
togglingToManualGasLimit: {
...expectedInitialState,
autoGasLimit: false
},
togglingToAutoGasLimit: {
...expectedInitialState,
autoGasLimit: true
},
settingLatestBlock: {
...expectedInitialState,
latestBlock: '12345'
}
};
const actions = {
changeLangauge: changeLanguage('langaugeToChange'),
toggleOffline: toggleOffline(),
toggleAutoGasLimit: toggleAutoGasLimit(),
setLatestBlock: setLatestBlock('12345')
};
describe('meta reducer', () => {
it('should return the inital state', () =>
expect(meta(undefined, {} as any)).toEqual(expectedState.initialState));
it('should handle toggling to offline', () =>
expect(meta(expectedState.initialState, actions.toggleOffline)).toEqual(
expectedState.togglingToOffline
));
it('should handle toggling back to online', () =>
expect(meta(expectedState.togglingToOffline, actions.toggleOffline)).toEqual(
expectedState.togglingToOnline
));
it('should handle toggling to manual gas limit', () =>
expect(meta(expectedState.initialState, actions.toggleAutoGasLimit)).toEqual(
expectedState.togglingToManualGasLimit
));
it('should handle toggling back to auto gas limit', () =>
expect(meta(expectedState.togglingToManualGasLimit, actions.toggleAutoGasLimit)).toEqual(
expectedState.togglingToAutoGasLimit
));
it('should handle setting the latest block', () =>
expect(meta(expectedState.initialState, actions.setLatestBlock)).toEqual(
expectedState.settingLatestBlock
));
});

View File

@ -0,0 +1,60 @@
import { CustomNetworkConfig } from 'types/network';
import { addCustomNetwork, removeCustomNetwork } from 'actions/config';
import { customNetworks } from 'reducers/config/networks/customNetworks';
const firstCustomNetworkId = 'firstCustomNetwork';
const firstCustomNetworkConfig: CustomNetworkConfig = {
isCustom: true,
chainId: 1,
name: firstCustomNetworkId,
unit: 'customNetworkUnit',
dPathFormats: null
};
const secondCustomNetworkId = 'secondCustomNetwork';
const secondCustomNetworkConfig: CustomNetworkConfig = {
...firstCustomNetworkConfig,
name: secondCustomNetworkId
};
const expectedState = {
initialState: {},
addFirstCustomNetwork: { [firstCustomNetworkId]: firstCustomNetworkConfig },
addSecondCustomNetwork: {
[firstCustomNetworkId]: firstCustomNetworkConfig,
[secondCustomNetworkId]: secondCustomNetworkConfig
},
removeFirstCustomNetwork: { [secondCustomNetworkId]: secondCustomNetworkConfig }
};
const actions = {
addFirstCustomNetwork: addCustomNetwork({
id: firstCustomNetworkId,
config: firstCustomNetworkConfig
}),
addSecondCustomNetwork: addCustomNetwork({
config: secondCustomNetworkConfig,
id: secondCustomNetworkId
}),
removeFirstCustomNetwork: removeCustomNetwork({ id: firstCustomNetworkId })
};
describe('custom networks reducer', () => {
it('should return the intial state', () =>
expect(customNetworks(undefined, {} as any)).toEqual(expectedState.initialState));
it('should handle adding the first custom network', () =>
expect(customNetworks(expectedState.initialState, actions.addFirstCustomNetwork)).toEqual(
expectedState.addFirstCustomNetwork
));
it('should handle adding the second custom network', () =>
expect(
customNetworks(expectedState.addFirstCustomNetwork, actions.addSecondCustomNetwork)
).toEqual(expectedState.addSecondCustomNetwork));
it('should handle removing the first custom network', () =>
expect(
customNetworks(expectedState.addSecondCustomNetwork, actions.removeFirstCustomNetwork)
).toEqual(expectedState.removeFirstCustomNetwork));
});

View File

@ -0,0 +1,16 @@
import { actions } from '../nodes/selectedNode.spec';
import { selectedNetwork } from 'reducers/config/networks/selectedNetwork';
const expectedState = {
initialState: 'ETH',
nodeChange: 'networkToChangeTo'
};
describe('selected network reducer', () => {
it('should return the initial state', () =>
expect(selectedNetwork(undefined, {} as any)).toEqual(expectedState.initialState));
it('should handle changing nodes by changing to the right network', () =>
expect(selectedNetwork(expectedState.initialState, actions.changeNode)).toEqual(
expectedState.nodeChange
));
});

View File

@ -0,0 +1,148 @@
import { staticNetworks, makeExplorer } from 'reducers/config/networks/staticNetworks';
import { ethPlorer, ETHTokenExplorer, SecureWalletName, InsecureWalletName } from 'config/data';
import { StaticNetworkConfig } from 'types/network';
import {
ETH_DEFAULT,
ETH_TREZOR,
ETH_LEDGER,
ETC_LEDGER,
ETC_TREZOR,
ETH_TESTNET,
EXP_DEFAULT,
UBQ_DEFAULT
} from 'config/dpaths';
const expectedInitialState = {
ETH: {
name: 'ETH',
unit: 'ETH',
chainId: 1,
isCustom: false,
color: '#0e97c0',
blockExplorer: makeExplorer('https://etherscan.io'),
tokenExplorer: {
name: ethPlorer,
address: ETHTokenExplorer
},
tokens: require('config/tokens/eth.json'),
contracts: require('config/contracts/eth.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TREZOR,
[SecureWalletName.LEDGER_NANO_S]: ETH_LEDGER,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_DEFAULT
}
},
Ropsten: {
name: 'Ropsten',
unit: 'ETH',
chainId: 3,
isCustom: false,
color: '#adc101',
blockExplorer: makeExplorer('https://ropsten.etherscan.io'),
tokens: require('config/tokens/ropsten.json'),
contracts: require('config/contracts/ropsten.json'),
isTestnet: true,
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TESTNET,
[SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET
}
},
Kovan: {
name: 'Kovan',
unit: 'ETH',
chainId: 42,
isCustom: false,
color: '#adc101',
blockExplorer: makeExplorer('https://kovan.etherscan.io'),
tokens: require('config/tokens/ropsten.json'),
contracts: require('config/contracts/ropsten.json'),
isTestnet: true,
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TESTNET,
[SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET
}
},
Rinkeby: {
name: 'Rinkeby',
unit: 'ETH',
chainId: 4,
isCustom: false,
color: '#adc101',
blockExplorer: makeExplorer('https://rinkeby.etherscan.io'),
tokens: require('config/tokens/rinkeby.json'),
contracts: require('config/contracts/rinkeby.json'),
isTestnet: true,
dPathFormats: {
[SecureWalletName.TREZOR]: ETH_TESTNET,
[SecureWalletName.LEDGER_NANO_S]: ETH_TESTNET,
[InsecureWalletName.MNEMONIC_PHRASE]: ETH_TESTNET
}
},
ETC: {
name: 'ETC',
unit: 'ETC',
chainId: 61,
isCustom: false,
color: '#669073',
blockExplorer: makeExplorer('https://gastracker.io'),
tokens: require('config/tokens/etc.json'),
contracts: require('config/contracts/etc.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: ETC_TREZOR,
[SecureWalletName.LEDGER_NANO_S]: ETC_LEDGER,
[InsecureWalletName.MNEMONIC_PHRASE]: ETC_TREZOR
}
},
UBQ: {
name: 'UBQ',
unit: 'UBQ',
chainId: 8,
isCustom: false,
color: '#b37aff',
blockExplorer: makeExplorer('https://ubiqscan.io/en'),
tokens: require('config/tokens/ubq.json'),
contracts: require('config/contracts/ubq.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: UBQ_DEFAULT,
[SecureWalletName.LEDGER_NANO_S]: UBQ_DEFAULT,
[InsecureWalletName.MNEMONIC_PHRASE]: UBQ_DEFAULT
}
},
EXP: {
name: 'EXP',
unit: 'EXP',
chainId: 2,
isCustom: false,
color: '#673ab7',
blockExplorer: makeExplorer('http://www.gander.tech'),
tokens: require('config/tokens/exp.json'),
contracts: require('config/contracts/exp.json'),
dPathFormats: {
[SecureWalletName.TREZOR]: EXP_DEFAULT,
[SecureWalletName.LEDGER_NANO_S]: EXP_DEFAULT,
[InsecureWalletName.MNEMONIC_PHRASE]: EXP_DEFAULT
}
}
};
const expectedState = {
initialState: expectedInitialState
};
describe('Testing contained data', () => {
it(`contain unique chainIds`, () => {
const networkValues = Object.values(expectedInitialState);
const chainIds = networkValues.map(a => a.chainId);
const chainIdsSet = new Set(chainIds);
expect(Array.from(chainIdsSet).length).toEqual(chainIds.length);
});
});
describe('static networks reducer', () => {
it('should return the initial state', () =>
expect(JSON.stringify(staticNetworks(undefined, {} as any))).toEqual(
JSON.stringify(expectedState.initialState)
));
});

View File

@ -0,0 +1,55 @@
import { addCustomNode, removeCustomNode } from 'actions/config';
import { CustomNodeConfig } from 'types/node';
import { customNodes } from 'reducers/config/nodes/customNodes';
const firstCustomNodeId = 'customNode1';
const firstCustomNode: CustomNodeConfig = {
isCustom: true,
id: firstCustomNodeId,
lib: jest.fn() as any,
name: 'My cool custom node',
network: 'CustomNetworkId',
port: 8080,
service: 'your custom node',
url: '127.0.0.1'
};
const secondCustomNodeId = 'customNode2';
const secondCustomNode: CustomNodeConfig = {
...firstCustomNode,
id: secondCustomNodeId
};
const expectedStates = {
initialState: {},
addFirstCustomNode: { [firstCustomNodeId]: firstCustomNode },
addSecondCustomNode: {
[firstCustomNodeId]: firstCustomNode,
[secondCustomNodeId]: secondCustomNode
},
removeFirstCustomNode: { [secondCustomNodeId]: secondCustomNode }
};
const actions = {
addFirstCustomNode: addCustomNode({ id: firstCustomNodeId, config: firstCustomNode }),
addSecondCustomNode: addCustomNode({ id: secondCustomNodeId, config: secondCustomNode }),
removeFirstCustomNode: removeCustomNode({ id: firstCustomNodeId })
};
describe('custom nodes reducer', () => {
it('should return the initial state', () =>
expect(customNodes(undefined, {} as any)).toEqual({}));
it('should handle adding the first custom node', () =>
expect(customNodes(expectedStates.initialState, actions.addFirstCustomNode)).toEqual(
expectedStates.addFirstCustomNode
));
it('should handle adding a second custom node', () =>
expect(customNodes(expectedStates.addFirstCustomNode, actions.addSecondCustomNode)).toEqual(
expectedStates.addSecondCustomNode
));
it('should handle removing the first custom node', () =>
expect(customNodes(expectedStates.addSecondCustomNode, actions.removeFirstCustomNode)).toEqual(
expectedStates.removeFirstCustomNode
));
});

View File

@ -0,0 +1,26 @@
import { changeNodeIntent, changeNode } from 'actions/config';
import { State, selectedNode } from 'reducers/config/nodes/selectedNode';
export const expectedState: { [key: string]: State } = {
initialState: { nodeId: 'eth_mew', pending: false },
nodeChange: { nodeId: 'nodeToChangeTo', pending: false },
nodeChangeIntent: { nodeId: 'eth_mew', pending: true }
};
export const actions = {
changeNode: changeNode({ nodeId: 'nodeToChangeTo', networkId: 'networkToChangeTo' }),
changeNodeIntent: changeNodeIntent('eth_mew')
};
describe('selected node reducer', () => {
it(' should return the initial state', () =>
expect(selectedNode(undefined, {} as any)).toEqual(expectedState.initialState));
it('should handle a node change', () =>
expect(selectedNode(undefined, actions.changeNode)).toEqual(expectedState.nodeChange));
it('should handle the intent to change a node', () =>
expect(selectedNode(expectedState.initialState, actions.changeNodeIntent)).toEqual(
expectedState.nodeChangeIntent
));
});

View File

@ -0,0 +1,128 @@
import { web3SetNode, web3UnsetNode } from 'actions/config';
import { staticNodes, INITIAL_STATE } from 'reducers/config/nodes/staticNodes';
import { EtherscanNode, InfuraNode, RPCNode } from 'libs/nodes';
import { Web3NodeConfig } from 'types/node';
import { networkIdToName } from 'libs/values';
import { Web3Service } from 'libs/nodes/web3';
const expectedInitialState = JSON.stringify({
eth_mew: {
network: 'ETH',
isCustom: false,
lib: new RPCNode('https://api.myetherapi.com/eth'),
service: 'MyEtherWallet',
estimateGas: true
},
eth_mycrypto: {
network: 'ETH',
isCustom: false,
lib: new RPCNode('https://api.mycryptoapi.com/eth'),
service: 'MyCrypto',
estimateGas: true
},
eth_ethscan: {
network: 'ETH',
isCustom: false,
service: 'Etherscan.io',
lib: new EtherscanNode('https://api.etherscan.io/api'),
estimateGas: false
},
eth_infura: {
network: 'ETH',
isCustom: false,
service: 'infura.io',
lib: new InfuraNode('https://mainnet.infura.io/mew'),
estimateGas: false
},
rop_mew: {
network: 'Ropsten',
isCustom: false,
service: 'MyEtherWallet',
lib: new RPCNode('https://api.myetherapi.com/rop'),
estimateGas: false
},
rop_infura: {
network: 'Ropsten',
isCustom: false,
service: 'infura.io',
lib: new InfuraNode('https://ropsten.infura.io/mew'),
estimateGas: false
},
kov_ethscan: {
network: 'Kovan',
isCustom: false,
service: 'Etherscan.io',
lib: new EtherscanNode('https://kovan.etherscan.io/api'),
estimateGas: false
},
rin_ethscan: {
network: 'Rinkeby',
isCustom: false,
service: 'Etherscan.io',
lib: new EtherscanNode('https://rinkeby.etherscan.io/api'),
estimateGas: false
},
rin_infura: {
network: 'Rinkeby',
isCustom: false,
service: 'infura.io',
lib: new InfuraNode('https://rinkeby.infura.io/mew'),
estimateGas: false
},
etc_epool: {
network: 'ETC',
isCustom: false,
service: 'Epool.io',
lib: new RPCNode('https://mewapi.epool.io'),
estimateGas: false
},
ubq: {
network: 'UBQ',
isCustom: false,
service: 'ubiqscan.io',
lib: new RPCNode('https://pyrus2.ubiqscan.io'),
estimateGas: true
},
exp_tech: {
network: 'EXP',
isCustom: false,
service: 'Expanse.tech',
lib: new RPCNode('https://node.expanse.tech/'),
estimateGas: true
}
});
const web3Id = 'web3';
const web3NetworkId = 1;
const web3Node: Web3NodeConfig = {
isCustom: false,
network: networkIdToName(web3NetworkId),
service: Web3Service,
lib: jest.fn() as any,
estimateGas: false,
hidden: true
};
const expectedStates = {
initialState: expectedInitialState,
setWeb3: { ...INITIAL_STATE, [web3Id]: web3Node },
unsetWeb3: { ...INITIAL_STATE }
};
const actions = {
web3SetNode: web3SetNode({ id: web3Id, config: web3Node }),
web3UnsetNode: web3UnsetNode()
};
describe('static nodes reducer', () => {
it('should return the inital state', () =>
// turn the JSON into a string because we're storing function in the state
expect(JSON.stringify(staticNodes(undefined, {} as any))).toEqual(expectedStates.initialState));
it('should handle setting the web3 node', () =>
expect(staticNodes(INITIAL_STATE, actions.web3SetNode)).toEqual(expectedStates.setWeb3));
it('should handle unsetting the web3 node', () =>
expect(staticNodes(expectedStates.setWeb3, actions.web3UnsetNode)).toEqual(
expectedStates.unsetWeb3
));
});

View File

@ -2,7 +2,7 @@ import { configuredStore } from 'store';
import { delay } from 'redux-saga'; import { delay } from 'redux-saga';
import { call, cancel, fork, put, take, select } from 'redux-saga/effects'; import { call, cancel, fork, put, take, select } from 'redux-saga/effects';
import { cloneableGenerator, createMockTask } from 'redux-saga/utils'; import { cloneableGenerator, createMockTask } from 'redux-saga/utils';
import { toggleOfflineConfig, changeNode, changeNodeIntent, setLatestBlock } from 'actions/config'; import { toggleOffline, changeNode, changeNodeIntent, setLatestBlock } from 'actions/config';
import { import {
pollOfflineStatus, pollOfflineStatus,
handlePollOfflineStatus, handlePollOfflineStatus,
@ -102,14 +102,14 @@ describe('pollOfflineStatus*', () => {
expect(data.gen.next(raceSuccess).value).toEqual( expect(data.gen.next(raceSuccess).value).toEqual(
put(showNotification('success', 'Your connection to the network has been restored!', 3000)) put(showNotification('success', 'Your connection to the network has been restored!', 3000))
); );
expect(data.gen.next().value).toEqual(put(toggleOfflineConfig())); expect(data.gen.next().value).toEqual(put(toggleOffline()));
}); });
it('should toggle offline and show notification if navigator agrees with isOffline and ping fails', () => { it('should toggle offline and show notification if navigator agrees with isOffline and ping fails', () => {
nav.onLine = isOffline; nav.onLine = isOffline;
expect(data.isOfflineClone.next(!isOffline)); expect(data.isOfflineClone.next(!isOffline));
expect(data.isOfflineClone.next(raceFailure).value).toMatchSnapshot(); expect(data.isOfflineClone.next(raceFailure).value).toMatchSnapshot();
expect(data.isOfflineClone.next().value).toEqual(put(toggleOfflineConfig())); expect(data.isOfflineClone.next().value).toEqual(put(toggleOffline()));
nav.onLine = !isOffline; nav.onLine = !isOffline;
}); });
}); });