Add reducer tests for new redux state
This commit is contained in:
parent
8048157448
commit
dccd976043
|
@ -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
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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}`,
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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
|
||||||
|
));
|
||||||
|
});
|
|
@ -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));
|
||||||
|
});
|
|
@ -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
|
||||||
|
));
|
||||||
|
});
|
|
@ -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)
|
||||||
|
));
|
||||||
|
});
|
|
@ -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
|
||||||
|
));
|
||||||
|
});
|
|
@ -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
|
||||||
|
));
|
||||||
|
});
|
|
@ -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
|
||||||
|
));
|
||||||
|
});
|
|
@ -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;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue