1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00
metamask-extension/test/unit/ui/app/actions.spec.js
Mark Stacey 20a7b8fa36
Force background state update after removing an account (#7840)
The e2e tests were failing intermittently after removing an account
because the account was shown as not deleted after the removal. I
suspect this was because the account _had_ been removed, but that
change to the background state hadn't yet propagated to the UI.

The background state is now synced before the loading overlay for
removing the account is removed, ensuring that the removed account
cannot be seen in the UI after removal.
2020-01-16 13:43:40 -04:00

1577 lines
45 KiB
JavaScript

/* eslint-disable */
// Used to inspect long objects
// util.inspect({JSON}, false, null))
// const util = require('util')
import assert from 'assert'
import sinon from 'sinon'
import clone from 'clone'
import nock from 'nock'
import fetchMock from 'fetch-mock'
import configureStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import EthQuery from 'eth-query'
import Eth from 'ethjs'
import KeyringController from 'eth-keyring-controller'
import { createTestProviderTools } from '../../../stub/provider'
const provider = createTestProviderTools({ scaffold: {}}).provider
import enLocale from '../../../../app/_locales/en/messages.json'
import * as actions from '../../../../ui/app/store/actions'
import MetaMaskController from '../../../../app/scripts/metamask-controller'
import firstTimeState from '../../localhostState'
import devState from '../../../data/2-state.json'
const middleware = [thunk]
const mockStore = configureStore(middleware)
describe('Actions', () => {
const noop = () => {}
const currentNetworkId = 42
let background, metamaskController
const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
const password = 'a-fake-password'
const importPrivkey = '4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553'
beforeEach(async () => {
metamaskController = new MetaMaskController({
provider,
keyringController: new KeyringController({}),
showUnapprovedTx: noop,
showUnconfirmedMessage: noop,
encryptor: {
encrypt: function (_, object) {
this.object = object
return Promise.resolve('mock-encrypted')
},
decrypt: function () {
return Promise.resolve(this.object)
},
},
initState: clone(firstTimeState),
})
metamaskController.threeBoxController = {
new3Box: sinon.spy(),
getThreeBoxSyncingState: sinon.spy(),
}
await metamaskController.createNewVaultAndRestore(password, TEST_SEED)
await metamaskController.importAccountWithStrategy('Private Key', [ importPrivkey ])
background = metamaskController.getApi()
actions._setBackgroundConnection(background)
global.ethQuery = new EthQuery(provider)
})
describe('#tryUnlockMetamask', () => {
let submitPasswordSpy, verifySeedPhraseSpy
afterEach(() => {
submitPasswordSpy.restore()
verifySeedPhraseSpy.restore()
})
it('calls submitPassword and verifySeedPhrase', async () => {
const store = mockStore({})
submitPasswordSpy = sinon.spy(background, 'submitPassword')
verifySeedPhraseSpy = sinon.spy(background, 'verifySeedPhrase')
await store.dispatch(actions.tryUnlockMetamask())
assert(submitPasswordSpy.calledOnce)
assert(verifySeedPhraseSpy.calledOnce)
})
it('errors on submitPassword will fail', async () => {
const store = mockStore({})
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'UNLOCK_IN_PROGRESS' },
{ type: 'UNLOCK_FAILED', value: 'error in submitPassword' },
{ type: 'HIDE_LOADING_INDICATION' },
]
submitPasswordSpy = sinon.stub(background, 'submitPassword')
submitPasswordSpy.callsFake((_, callback) => {
callback(new Error('error in submitPassword'))
})
try {
await store.dispatch(actions.tryUnlockMetamask('test'))
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
it('displays warning error and unlock failed when verifySeed fails', async () => {
const store = mockStore({})
const displayWarningError = [ { type: 'DISPLAY_WARNING', value: 'error' } ]
const unlockFailedError = [ { type: 'UNLOCK_FAILED', value: 'error' } ]
verifySeedPhraseSpy = sinon.stub(background, 'verifySeedPhrase')
verifySeedPhraseSpy.callsFake(callback => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.tryUnlockMetamask('test'))
assert.fail('Should have thrown error')
} catch (_) {
const actions = store.getActions()
const warning = actions.filter(action => action.type === 'DISPLAY_WARNING')
const unlockFailed = actions.filter(action => action.type === 'UNLOCK_FAILED')
assert.deepEqual(warning, displayWarningError)
assert.deepEqual(unlockFailed, unlockFailedError)
}
})
})
describe('#createNewVaultAndRestore', () => {
let createNewVaultAndRestoreSpy
afterEach(() => {
createNewVaultAndRestoreSpy.restore()
})
it('restores new vault', async () => {
const store = mockStore({})
createNewVaultAndRestoreSpy = sinon.spy(background, 'createNewVaultAndRestore')
try {
await store.dispatch(actions.createNewVaultAndRestore())
assert.fail('Should have thrown error')
} catch (_) {
assert(createNewVaultAndRestoreSpy.calledOnce)
}
})
it('errors when callback in createNewVaultAndRestore throws', async () => {
const store = mockStore({})
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'DISPLAY_WARNING', value: 'error' },
{ type: 'HIDE_LOADING_INDICATION' },
]
createNewVaultAndRestoreSpy = sinon.stub(background, 'createNewVaultAndRestore')
createNewVaultAndRestoreSpy.callsFake((_, __, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.createNewVaultAndRestore())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#requestRevealSeedWords', () => {
let submitPasswordSpy
afterEach(() => {
submitPasswordSpy.restore()
})
it('calls submitPassword in background', async () => {
const store = mockStore()
submitPasswordSpy = sinon.spy(background, 'verifySeedPhrase')
await store.dispatch(actions.requestRevealSeedWords())
assert(submitPasswordSpy.calledOnce)
})
it('displays warning error message then callback in background errors', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
submitPasswordSpy = sinon.stub(background, 'verifySeedPhrase')
submitPasswordSpy.callsFake((callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.requestRevealSeedWords())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#removeAccount', () => {
let removeAccountSpy
afterEach(() => {
removeAccountSpy.restore()
})
it('calls removeAccount in background and expect actions to show account', async () => {
const store = mockStore(devState)
const expectedActions = [
'SHOW_LOADING_INDICATION',
'UPDATE_METAMASK_STATE',
'HIDE_LOADING_INDICATION',
'SHOW_ACCOUNTS_PAGE',
]
removeAccountSpy = sinon.spy(background, 'removeAccount')
await store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
assert(removeAccountSpy.calledOnce)
const actionTypes = store
.getActions()
.map(action => action.type)
assert.deepEqual(actionTypes, expectedActions)
})
it('displays warning error message when removeAccount callback errors', async () => {
const store = mockStore()
const expectedActions = [
'SHOW_LOADING_INDICATION',
'DISPLAY_WARNING',
'HIDE_LOADING_INDICATION',
]
removeAccountSpy = sinon.stub(background, 'removeAccount')
removeAccountSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
assert.fail('Should have thrown error')
} catch (_) {
const actionTypes = store
.getActions()
.map(action => action.type)
assert.deepEqual(actionTypes, expectedActions)
}
})
})
describe('#addNewKeyring', () => {
let addNewKeyringSpy
beforeEach(() => {
addNewKeyringSpy = sinon.stub(background, 'addNewKeyring')
})
afterEach(() => {
addNewKeyringSpy.restore()
})
it('calls addNewKeyring', () => {
const privateKey = 'c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
const store = mockStore()
store.dispatch(actions.addNewKeyring('Simple Key Pair', [ privateKey ]))
assert(addNewKeyringSpy.calledOnce)
})
it('errors then addNewKeyring in background throws', () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
addNewKeyringSpy.callsFake((_, __, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.addNewKeyring())
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#resetAccount', () => {
let resetAccountSpy
afterEach(() => {
resetAccountSpy.restore()
})
it('resets account', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'SHOW_ACCOUNTS_PAGE' },
]
resetAccountSpy = sinon.spy(background, 'resetAccount')
await store.dispatch(actions.resetAccount())
assert(resetAccountSpy.calledOnce)
assert.deepEqual(store.getActions(), expectedActions)
})
it('throws if resetAccount throws', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
resetAccountSpy = sinon.stub(background, 'resetAccount')
resetAccountSpy.callsFake((callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.resetAccount())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#importNewAccount', () => {
let importAccountWithStrategySpy
afterEach(() => {
importAccountWithStrategySpy.restore()
})
it('calls importAccountWithStrategies in background', () => {
const store = mockStore()
importAccountWithStrategySpy = sinon.spy(background, 'importAccountWithStrategy')
const importPrivkey = 'c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3'
store.dispatch(actions.importNewAccount('Private Key', [ importPrivkey ]))
assert(importAccountWithStrategySpy.calledOnce)
})
it('displays warning error message when importAccount in background callback errors', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: 'This may take a while, please be patient.' },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
importAccountWithStrategySpy = sinon.stub(background, 'importAccountWithStrategy')
importAccountWithStrategySpy.callsFake((_, __, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.importNewAccount())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#addNewAccount', () => {
it('Adds a new account', () => {
const store = mockStore({ metamask: devState })
const addNewAccountSpy = sinon.spy(background, 'addNewAccount')
store.dispatch(actions.addNewAccount())
assert(addNewAccountSpy.calledOnce)
})
})
describe('#checkHardwareStatus', () => {
let checkHardwareStatusSpy
beforeEach(() => {
checkHardwareStatusSpy = sinon.stub(background, 'checkHardwareStatus')
})
afterEach(() => {
checkHardwareStatusSpy.restore()
})
it('calls checkHardwareStatus in background', async () => {
const store = mockStore()
store.dispatch(await actions.checkHardwareStatus('ledger', `m/44'/60'/0'/0`))
assert.equal(checkHardwareStatusSpy.calledOnce, true)
})
it('shows loading indicator and displays error', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
checkHardwareStatusSpy.callsFake((deviceName, hdPath, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.checkHardwareStatus())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#forgetDevice', () => {
let forgetDeviceSpy
beforeEach(() => {
forgetDeviceSpy = sinon.stub(background, 'forgetDevice')
})
afterEach(() => {
forgetDeviceSpy.restore()
})
it('calls forgetDevice in background', () => {
const store = mockStore()
store.dispatch(actions.forgetDevice('ledger'))
assert.equal(forgetDeviceSpy.calledOnce, true)
})
it('shows loading indicator and displays error', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
forgetDeviceSpy.callsFake((deviceName, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.forgetDevice())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#connectHardware', () => {
let connectHardwareSpy
beforeEach(() => {
connectHardwareSpy = sinon.stub(background, 'connectHardware')
})
afterEach(() => {
connectHardwareSpy.restore()
})
it('calls connectHardware in background', () => {
const store = mockStore()
store.dispatch(actions.connectHardware('ledger', 0, `m/44'/60'/0'/0`))
assert.equal(connectHardwareSpy.calledOnce, true)
})
it('shows loading indicator and displays error', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
connectHardwareSpy.callsFake((deviceName, page, hdPath, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.connectHardware())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#unlockHardwareWalletAccount', () => {
let unlockHardwareWalletAccountSpy
beforeEach(() => {
unlockHardwareWalletAccountSpy = sinon.stub(background, 'unlockHardwareWalletAccount')
})
afterEach(() => {
unlockHardwareWalletAccountSpy.restore()
})
it('calls unlockHardwareWalletAccount in background', () => {
const store = mockStore()
store.dispatch(actions.unlockHardwareWalletAccount('ledger', 0, `m/44'/60'/0'/0`))
assert.equal(unlockHardwareWalletAccountSpy.calledOnce, true)
})
it('shows loading indicator and displays error', async() => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
unlockHardwareWalletAccountSpy.callsFake((deviceName, page, hdPath, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.unlockHardwareWalletAccount())
assert.fail('Should have thrown error')
} catch (error) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#setCurrentCurrency', () => {
let setCurrentCurrencySpy
beforeEach(() => {
setCurrentCurrencySpy = sinon.stub(background, 'setCurrentCurrency')
})
afterEach(() => {
setCurrentCurrencySpy.restore()
})
it('calls setCurrentCurrency', () => {
const store = mockStore()
store.dispatch(actions.setCurrentCurrency('jpy'))
assert(setCurrentCurrencySpy.calledOnce)
})
it('throws if setCurrentCurrency throws', () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
setCurrentCurrencySpy.callsFake((_, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.setCurrentCurrency())
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#signMsg', () => {
let signMessageSpy, metamaskMsgs, msgId, messages
const msgParams = {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
}
beforeEach(() => {
metamaskController.newUnsignedMessage(msgParams, noop)
metamaskMsgs = metamaskController.messageManager.getUnapprovedMsgs()
messages = metamaskController.messageManager.messages
msgId = Object.keys(metamaskMsgs)[0]
messages[0].msgParams.metamaskId = parseInt(msgId)
})
afterEach(() => {
signMessageSpy.restore()
})
it('calls signMsg in background', () => {
const store = mockStore()
signMessageSpy = sinon.spy(background, 'signMessage')
store.dispatch(actions.signMsg(msgParams))
assert(signMessageSpy.calledOnce)
})
it('errors when signMessage in background throws', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'UPDATE_METAMASK_STATE', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
signMessageSpy = sinon.stub(background, 'signMessage')
signMessageSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.signMsg())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#signPersonalMsg', () => {
let signPersonalMessageSpy, metamaskMsgs, msgId, personalMessages
const msgParams = {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0',
}
beforeEach(() => {
metamaskController.newUnsignedPersonalMessage(msgParams, noop)
metamaskMsgs = metamaskController.personalMessageManager.getUnapprovedMsgs()
personalMessages = metamaskController.personalMessageManager.messages
msgId = Object.keys(metamaskMsgs)[0]
personalMessages[0].msgParams.metamaskId = parseInt(msgId)
})
afterEach(() => {
signPersonalMessageSpy.restore()
})
it('calls signPersonalMessage', () => {
const store = mockStore()
signPersonalMessageSpy = sinon.spy(background, 'signPersonalMessage')
store.dispatch(actions.signPersonalMsg(msgParams))
assert(signPersonalMessageSpy.calledOnce)
})
it('throws if signPersonalMessage throws', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'UPDATE_METAMASK_STATE', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
signPersonalMessageSpy = sinon.stub(background, 'signPersonalMessage')
signPersonalMessageSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.signPersonalMsg(msgParams))
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#signTypedMsg', () => {
let signTypedMsgSpy, messages, typedMessages, msgId
const msgParamsV3 = {
from: '0x0DCD5D886577d5081B0c52e242Ef29E70Be3E7bc',
data: JSON.stringify({
'types': {
'EIP712Domain': [
{'name': 'name', 'type': 'string'},
{'name': 'version', 'type': 'string'},
{'name': 'chainId', 'type': 'uint256'},
{'name': 'verifyingContract', 'type': 'address'},
],
'Person': [
{'name': 'name', 'type': 'string'},
{'name': 'wallet', 'type': 'address'},
],
'Mail': [
{'name': 'from', 'type': 'Person'},
{'name': 'to', 'type': 'Person'},
{'name': 'contents', 'type': 'string'},
],
},
'primaryType': 'Mail',
'domain': {
'name': 'Ether Mainl',
'version': '1',
'verifyingContract': '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
'message': {
'from': {
'name': 'Cow',
'wallet': '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
'to': {
'name': 'Bob',
'wallet': '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
'contents': 'Hello, Bob!',
},
}),
}
beforeEach(() => {
metamaskController.newUnsignedTypedMessage(msgParamsV3, null, 'V3')
messages = metamaskController.typedMessageManager.getUnapprovedMsgs()
typedMessages = metamaskController.typedMessageManager.messages
msgId = Object.keys(messages)[0]
typedMessages[0].msgParams.metamaskId = parseInt(msgId)
signTypedMsgSpy = sinon.stub(background, 'signTypedMessage')
})
afterEach(() => {
signTypedMsgSpy.restore()
})
it('calls signTypedMsg in background with no error', () => {
const store = mockStore()
store.dispatch(actions.signTypedMsg(msgParamsV3))
assert(signTypedMsgSpy.calledOnce)
})
it('returns expected actions with error', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'UPDATE_METAMASK_STATE', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
signTypedMsgSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.signTypedMsg())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#signTx', () => {
let sendTransactionSpy
beforeEach(() => {
sendTransactionSpy = sinon.stub(global.ethQuery, 'sendTransaction')
})
afterEach(() => {
sendTransactionSpy.restore()
})
it('calls sendTransaction in global ethQuery', () => {
const store = mockStore()
store.dispatch(actions.signTx())
assert(sendTransactionSpy.calledOnce)
})
it('errors in when sendTransaction throws', () => {
const store = mockStore()
const expectedActions = [
{ type: 'DISPLAY_WARNING', value: 'error' },
{ type: 'SHOW_CONF_TX_PAGE', transForward: true, id: undefined },
]
sendTransactionSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.signTx())
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#updatedGasData', () => {
it('errors when get code does not return', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'GAS_LOADING_STARTED' },
{ type: 'UPDATE_SEND_ERRORS', value: { gasLoadingError: 'gasLoadingError' } },
{ type: 'GAS_LOADING_FINISHED' },
]
const mockData = {
gasPrice: '0x3b9aca00', //
blockGasLimit: '0x6ad79a', // 7002010
selectedAddress: '0x0DCD5D886577d5081B0c52e242Ef29E70Be3E7bc',
to: '0xEC1Adf982415D2Ef5ec55899b9Bfb8BC0f29251B',
value: '0xde0b6b3a7640000', // 1000000000000000000
}
try {
await store.dispatch(actions.updateGasData(mockData))
assert.fail('Should have thrown error')
} catch (error) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#updatedGasData', () => {
const stub = sinon.stub().returns('0x')
const mockData = {
gasPrice: '0x3b9aca00', //
blockGasLimit: '0x6ad79a', // 7002010
selectedAddress: '0x0DCD5D886577d5081B0c52e242Ef29E70Be3E7bc',
to: '0xEC1Adf982415D2Ef5ec55899b9Bfb8BC0f29251B',
value: '0xde0b6b3a7640000', // 1000000000000000000
}
beforeEach(() => {
global.eth = {
getCode: stub,
}
})
afterEach(() => {
stub.reset()
})
it('returns default gas limit for basic eth transaction', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'GAS_LOADING_STARTED' },
{ type: 'UPDATE_GAS_LIMIT', value: '0x5208' },
{ type: 'metamask/gas/SET_CUSTOM_GAS_LIMIT', value: '0x5208' },
{ type: 'UPDATE_SEND_ERRORS', value: { gasLoadingError: null } },
{ type: 'GAS_LOADING_FINISHED' },
]
await store.dispatch(actions.updateGasData(mockData))
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#signTokenTx', () => {
let tokenSpy
beforeEach(() => {
global.eth = new Eth(provider)
tokenSpy = sinon.spy(global.eth, 'contract')
})
afterEach(() => {
tokenSpy.restore()
})
it('calls eth.contract', () => {
const store = mockStore()
store.dispatch(actions.signTokenTx())
assert(tokenSpy.calledOnce)
})
})
describe('#updateTransaction', () => {
let updateTransactionSpy
const txParams = {
'from': '0x1',
'gas': '0x5208',
'gasPrice': '0x3b9aca00',
'to': '0x2',
'value': '0x0',
}
const txData = { id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: txParams }
beforeEach( async () => {
await metamaskController.txController.txStateManager.addTx(txData)
})
afterEach(() => {
updateTransactionSpy.restore()
})
it('updates transaction', async () => {
const store = mockStore()
updateTransactionSpy = sinon.spy(background, 'updateTransaction')
await store.dispatch(actions.updateTransaction(txData))
const resultantActions = store.getActions()
assert.ok(updateTransactionSpy.calledOnce)
assert.deepEqual(resultantActions[1], { type: 'UPDATE_TRANSACTION_PARAMS', id: txData.id, value: txParams })
})
it('rejects with error message', async () => {
const store = mockStore()
updateTransactionSpy = sinon.stub(background, 'updateTransaction')
updateTransactionSpy.callsFake((res, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.updateTransaction(txData))
assert.fail('Should have thrown error')
} catch (error) {
assert.equal(error.message, 'error')
}
})
})
describe('#lockMetamask', () => {
let backgroundSetLockedSpy
afterEach(() => {
backgroundSetLockedSpy.restore()
})
it('calls setLocked', async () => {
const store = mockStore()
backgroundSetLockedSpy = sinon.spy(background, 'setLocked')
await store.dispatch(actions.lockMetamask())
assert(backgroundSetLockedSpy.calledOnce)
})
it('returns display warning error with value when setLocked in background callback errors', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'DISPLAY_WARNING', value: 'error' },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'LOCK_METAMASK' },
]
backgroundSetLockedSpy = sinon.stub(background, 'setLocked')
backgroundSetLockedSpy.callsFake(callback => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.lockMetamask())
assert.fail('Should have thrown error')
} catch (error) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#setSelectedAddress', () => {
let setSelectedAddressSpy
beforeEach(() => {
setSelectedAddressSpy = sinon.stub(background, 'setSelectedAddress')
})
afterEach(() => {
setSelectedAddressSpy.restore()
})
it('calls setSelectedAddress in background', () => {
const store = mockStore({ metamask: devState })
store.dispatch(actions.setSelectedAddress('0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
assert(setSelectedAddressSpy.calledOnce)
})
it('errors when setSelectedAddress throws', () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
setSelectedAddressSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.setSelectedAddress())
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#showAccountDetail', () => {
let setSelectedAddressSpy
beforeEach(() => {
setSelectedAddressSpy = sinon.stub(background, 'setSelectedAddress')
})
afterEach(() => {
setSelectedAddressSpy.restore()
})
it('#showAccountDetail', () => {
const store = mockStore()
store.dispatch(actions.showAccountDetail())
assert(setSelectedAddressSpy.calledOnce)
})
it('displays warning if setSelectedAddress throws', () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
setSelectedAddressSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.showAccountDetail())
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#addToken', () => {
let addTokenSpy
beforeEach(() => {
addTokenSpy = sinon.stub(background, 'addToken')
})
afterEach(() => {
addTokenSpy.restore()
})
it('calls addToken in background', async () => {
const store = mockStore()
store.dispatch(actions.addToken())
assert(addTokenSpy.calledOnce)
})
it('errors when addToken in background throws', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
addTokenSpy.callsFake((_, __, ___, ____, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.addToken())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#removeToken', () => {
let removeTokenSpy
beforeEach(() => {
removeTokenSpy = sinon.stub(background, 'removeToken')
})
afterEach(() => {
removeTokenSpy.restore()
})
it('calls removeToken in background', async () => {
const store = mockStore()
store.dispatch(await actions.removeToken())
assert(removeTokenSpy.calledOnce)
})
it('errors when removeToken in background fails', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
removeTokenSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.removeToken())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#setProviderType', () => {
let setProviderTypeSpy
let store
beforeEach(() => {
store = mockStore({ metamask: { provider: {} } })
setProviderTypeSpy = sinon.stub(background, 'setProviderType')
})
afterEach(() => {
setProviderTypeSpy.restore()
})
it('calls setProviderType', () => {
store.dispatch(actions.setProviderType())
assert(setProviderTypeSpy.calledOnce)
})
it('displays warning when setProviderType throws', () => {
const expectedActions = [
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
]
setProviderTypeSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.setProviderType())
assert(setProviderTypeSpy.calledOnce)
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#setRpcTarget', () => {
let setRpcTargetSpy
beforeEach(() => {
setRpcTargetSpy = sinon.stub(background, 'setCustomRpc')
})
afterEach(() => {
setRpcTargetSpy.restore()
})
it('calls setRpcTarget', () => {
const store = mockStore()
store.dispatch(actions.setRpcTarget('http://localhost:8545'))
assert(setRpcTargetSpy.calledOnce)
})
it('displays warning when setRpcTarget throws', () => {
const store = mockStore()
const expectedActions = [
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
]
setRpcTargetSpy.callsFake((_, __, ___, ____, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.setRpcTarget())
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#addToAddressBook', () => {
let addToAddressBookSpy
beforeEach(() => {
addToAddressBookSpy = sinon.stub(background, 'setAddressBook')
})
afterEach(() => {
addToAddressBookSpy.restore()
})
it('calls setAddressBook', () => {
const store = mockStore({ metamask: devState })
store.dispatch(actions.addToAddressBook('test'))
assert(addToAddressBookSpy.calledOnce)
})
})
describe('#exportAccount', () => {
let submitPasswordSpy, exportAccountSpy
afterEach(() => {
submitPasswordSpy.restore()
exportAccountSpy.restore()
})
it('returns expected actions for successful action', async () => {
const store = mockStore(devState)
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'SHOW_PRIVATE_KEY', value: '7ec73b91bb20f209a7ff2d32f542c3420b4fccf14abcc7840d2eff0ebcb18505' },
]
submitPasswordSpy = sinon.spy(background, 'submitPassword')
exportAccountSpy = sinon.spy(background, 'exportAccount')
await store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
assert(submitPasswordSpy.calledOnce)
assert(exportAccountSpy.calledOnce)
assert.deepEqual(store.getActions(), expectedActions)
})
it('returns action errors when first func callback errors', async () => {
const store = mockStore(devState)
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'Incorrect Password.' },
]
submitPasswordSpy = sinon.stub(background, 'submitPassword')
submitPasswordSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
it('returns action errors when second func callback errors', async () => {
const store = mockStore(devState)
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'Had a problem exporting the account.' },
]
exportAccountSpy = sinon.stub(background, 'exportAccount')
exportAccountSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#setAccountLabel', () => {
let setAccountLabelSpy
beforeEach(() => {
setAccountLabelSpy = sinon.stub(background, 'setAccountLabel')
})
it('calls setAccountLabel', () => {
const store = mockStore()
store.dispatch(actions.setAccountLabel('0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', 'test'))
assert(setAccountLabelSpy.calledOnce)
})
})
describe('#pairUpdate', () => {
beforeEach(() => {
nock('https://shapeshift.io')
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
.get('/marketinfo/btc_eth')
.reply(200, {pair: 'BTC_ETH', rate: 25.68289016, minerFee: 0.00176, limit: 0.67748474, minimum: 0.00013569, maxLimit: 0.67758573})
nock('https://shapeshift.io')
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
.get('/coins')
.reply(200)
})
it('calls expected actions', () => {
const store = mockStore()
// issue with dispatch action in callback not showing
const expectedActions = [
{ type: 'SHOW_SUB_LOADING_INDICATION' },
{ type: 'HIDE_WARNING' },
]
store.dispatch(actions.pairUpdate('btc'))
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#setFeatureFlag', () => {
let setFeatureFlagSpy
beforeEach(() => {
setFeatureFlagSpy = sinon.stub(background, 'setFeatureFlag')
})
afterEach(() => {
setFeatureFlagSpy.restore()
})
it('calls setFeatureFlag in the background', () => {
const store = mockStore()
store.dispatch(actions.setFeatureFlag())
assert(setFeatureFlagSpy.calledOnce)
})
it('errors when setFeatureFlag in background throws', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
setFeatureFlagSpy.callsFake((_, __, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.setFeatureFlag())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#setCompletedOnboarding', () => {
let completeOnboardingSpy
beforeEach(() => {
completeOnboardingSpy = sinon.stub(background, 'completeOnboarding')
completeOnboardingSpy.callsFake(cb => cb())
})
after(() => {
completeOnboardingSpy.restore()
})
it('completes onboarding', async () => {
const store = mockStore()
await store.dispatch(actions.setCompletedOnboarding())
assert.equal(completeOnboardingSpy.callCount, 1)
})
})
describe('#updateNetworkNonce', () => {
let getTransactionCountSpy
afterEach(() => {
getTransactionCountSpy.restore()
})
it('calls getTransactionCount', () => {
const store = mockStore()
getTransactionCountSpy = sinon.spy(global.ethQuery, 'getTransactionCount')
store.dispatch(actions.updateNetworkNonce())
assert(getTransactionCountSpy.calledOnce)
})
it('errors when getTransactionCount throws', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'DISPLAY_WARNING', value: 'error' },
]
getTransactionCountSpy = sinon.stub(global.ethQuery, 'getTransactionCount')
getTransactionCountSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.updateNetworkNonce())
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#setUseBlockie', () => {
let setUseBlockieSpy
beforeEach(() => {
setUseBlockieSpy = sinon.stub(background, 'setUseBlockie')
})
afterEach(() => {
setUseBlockieSpy.restore()
})
it('calls setUseBlockie in background', () => {
const store = mockStore()
store.dispatch(actions.setUseBlockie())
assert(setUseBlockieSpy.calledOnce)
})
it('errors when setUseBlockie in background throws', () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
{ type: 'SET_USE_BLOCKIE', value: undefined },
]
setUseBlockieSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
store.dispatch(actions.setUseBlockie())
assert.deepEqual(store.getActions(), expectedActions)
})
})
describe('#updateCurrentLocale', () => {
let setCurrentLocaleSpy
beforeEach(() => {
fetchMock.get('*', enLocale)
})
afterEach(() => {
setCurrentLocaleSpy.restore()
fetchMock.restore()
})
it('calls expected actions', async () => {
const store = mockStore()
setCurrentLocaleSpy = sinon.spy(background, 'setCurrentLocale')
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'SET_CURRENT_LOCALE', value: { locale: 'en', messages: enLocale }},
{ type: 'HIDE_LOADING_INDICATION' },
]
await store.dispatch(actions.updateCurrentLocale('en'))
assert(setCurrentLocaleSpy.calledOnce)
assert.deepEqual(store.getActions(), expectedActions)
})
it('errors when setCurrentLocale throws', async () => {
const store = mockStore()
const expectedActions = [
{ type: 'SHOW_LOADING_INDICATION', value: undefined },
{ type: 'HIDE_LOADING_INDICATION' },
{ type: 'DISPLAY_WARNING', value: 'error' },
]
setCurrentLocaleSpy = sinon.stub(background, 'setCurrentLocale')
setCurrentLocaleSpy.callsFake((_, callback) => {
callback(new Error('error'))
})
try {
await store.dispatch(actions.updateCurrentLocale('en'))
assert.fail('Should have thrown error')
} catch (_) {
assert.deepEqual(store.getActions(), expectedActions)
}
})
})
describe('#markPasswordForgotten', () => {
it('calls markPasswordForgotten', () => {
const store = mockStore()
const markPasswordForgottenSpy = sinon.stub(background, 'markPasswordForgotten').callsArg(0)
store.dispatch(actions.markPasswordForgotten())
const resultantActions = store.getActions()
assert.deepEqual(resultantActions[1], { type: 'FORGOT_PASSWORD', value: true })
assert.ok(markPasswordForgottenSpy.calledOnce)
markPasswordForgottenSpy.restore()
})
})
describe('#unMarkPasswordForgotten', () => {
it('calls unMarkPasswordForgotten', async () => {
const store = mockStore()
const unMarkPasswordForgottenSpy = sinon.stub(background, 'unMarkPasswordForgotten').callsArg(0)
await store.dispatch(actions.unMarkPasswordForgotten())
const resultantActions = store.getActions()
assert.deepEqual(resultantActions[0], { type: 'FORGOT_PASSWORD', value: false })
assert.ok(unMarkPasswordForgottenSpy.calledOnce)
unMarkPasswordForgottenSpy.restore()
})
})
})