aboutsummaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
authorKevin Serrano <kevgagser@gmail.com>2018-10-15 08:32:29 +0800
committerGitHub <noreply@github.com>2018-10-15 08:32:29 +0800
commit85884b21afe6e5e85b58123b24e72776d1437cc6 (patch)
tree6dbec947089691c0dffd5febbe6e92c1f712e40f /test/unit
parentdb981b827b07c946e17d3a8aeaefd2143c236ef7 (diff)
parent7f6b488c04e6ea12b2ef24ac936b6a236ad904c2 (diff)
downloadtangerine-wallet-browser-85884b21afe6e5e85b58123b24e72776d1437cc6.tar.gz
tangerine-wallet-browser-85884b21afe6e5e85b58123b24e72776d1437cc6.tar.zst
tangerine-wallet-browser-85884b21afe6e5e85b58123b24e72776d1437cc6.zip
Merge pull request #5512 from MetaMask/v4.14.0
Version 4.14.0
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/app/cleanErrorStack.spec.js33
-rw-r--r--test/unit/app/controllers/blacklist-controller-test.js15
-rw-r--r--test/unit/app/controllers/currency-controller-test.js3
-rw-r--r--test/unit/app/controllers/detect-tokens-test.js51
-rw-r--r--test/unit/app/controllers/metamask-controller-test.js248
-rw-r--r--test/unit/app/controllers/network-contoller-test.js7
-rw-r--r--test/unit/app/controllers/notice-controller-test.js7
-rw-r--r--test/unit/app/controllers/preferences-controller-test.js140
-rw-r--r--test/unit/app/controllers/transactions/nonce-tracker-test.js9
-rw-r--r--test/unit/app/controllers/transactions/pending-tx-test.js78
-rw-r--r--test/unit/app/controllers/transactions/tx-controller-test.js100
-rw-r--r--test/unit/components/balance-component-test.js3
-rw-r--r--test/unit/components/binary-renderer-test.js2
-rw-r--r--test/unit/config-manager-test.js115
-rw-r--r--test/unit/development/sample-changelog.md4
-rw-r--r--test/unit/localhostState.js21
-rw-r--r--test/unit/responsive/components/dropdown-test.js2
-rw-r--r--test/unit/ui/add-token.spec.js2
-rw-r--r--test/unit/ui/app/actions.spec.js1468
-rw-r--r--test/unit/ui/app/components/identicon.spec.js36
-rw-r--r--test/unit/ui/app/components/token-cell.spec.js69
-rw-r--r--test/unit/ui/app/selectors.spec.js175
-rw-r--r--test/unit/ui/etherscan-prefix-for-network.spec.js26
23 files changed, 2298 insertions, 316 deletions
diff --git a/test/unit/app/cleanErrorStack.spec.js b/test/unit/app/cleanErrorStack.spec.js
new file mode 100644
index 000000000..7a1ab1ed8
--- /dev/null
+++ b/test/unit/app/cleanErrorStack.spec.js
@@ -0,0 +1,33 @@
+const assert = require('assert')
+const cleanErrorStack = require('../../../app/scripts/lib/cleanErrorStack')
+
+describe('Clean Error Stack', () => {
+
+ const testMessage = 'Test Message'
+ const testError = new Error(testMessage)
+ const undefinedErrorName = new Error(testMessage)
+ const blankErrorName = new Error(testMessage)
+ const blankMsgError = new Error()
+
+ beforeEach(() => {
+ undefinedErrorName.name = undefined
+ blankErrorName.name = ''
+ })
+
+ it('tests error with message', () => {
+ assert.equal(cleanErrorStack(testError), 'Error: Test Message')
+ })
+
+ it('tests error with undefined name', () => {
+ assert.equal(cleanErrorStack(undefinedErrorName).toString(), 'Error: Test Message')
+ })
+
+ it('tests error with blank name', () => {
+ assert.equal(cleanErrorStack(blankErrorName).toString(), 'Test Message')
+ })
+
+ it('tests error with blank message', () => {
+ assert.equal(cleanErrorStack(blankMsgError), 'Error')
+ })
+
+})
diff --git a/test/unit/app/controllers/blacklist-controller-test.js b/test/unit/app/controllers/blacklist-controller-test.js
index 085641777..7a14c02cc 100644
--- a/test/unit/app/controllers/blacklist-controller-test.js
+++ b/test/unit/app/controllers/blacklist-controller-test.js
@@ -8,6 +8,16 @@ describe('blacklist controller', function () {
blacklistController = new BlacklistController()
})
+ describe('whitelistDomain', function () {
+ it('should add hostname to the runtime whitelist', function () {
+ blacklistController.whitelistDomain('foo.com')
+ assert.deepEqual(blacklistController.store.getState().whitelist, ['foo.com'])
+
+ blacklistController.whitelistDomain('bar.com')
+ assert.deepEqual(blacklistController.store.getState().whitelist, ['bar.com', 'foo.com'])
+ })
+ })
+
describe('checkForPhishing', function () {
it('should not flag whitelisted values', function () {
const result = blacklistController.checkForPhishing('www.metamask.io')
@@ -37,5 +47,10 @@ describe('blacklist controller', function () {
const result = blacklistController.checkForPhishing('zero-faucet.metamask.io')
assert.equal(result, false)
})
+ it('should not flag whitelisted domain', function () {
+ blacklistController.whitelistDomain('metamask.com')
+ const result = blacklistController.checkForPhishing('metamask.com')
+ assert.equal(result, false)
+ })
})
})
diff --git a/test/unit/app/controllers/currency-controller-test.js b/test/unit/app/controllers/currency-controller-test.js
index 1941d1c43..7c4644d9f 100644
--- a/test/unit/app/controllers/currency-controller-test.js
+++ b/test/unit/app/controllers/currency-controller-test.js
@@ -1,6 +1,3 @@
-// polyfill fetch
-global.fetch = global.fetch || require('isomorphic-fetch')
-
const assert = require('assert')
const nock = require('nock')
const CurrencyController = require('../../../../app/scripts/controllers/currency')
diff --git a/test/unit/app/controllers/detect-tokens-test.js b/test/unit/app/controllers/detect-tokens-test.js
index d6c3fad8a..2acc53e92 100644
--- a/test/unit/app/controllers/detect-tokens-test.js
+++ b/test/unit/app/controllers/detect-tokens-test.js
@@ -1,4 +1,5 @@
const assert = require('assert')
+const nock = require('nock')
const sinon = require('sinon')
const ObservableStore = require('obs-store')
const DetectTokensController = require('../../../../app/scripts/controllers/detect-tokens')
@@ -6,15 +7,34 @@ const NetworkController = require('../../../../app/scripts/controllers/network/n
const PreferencesController = require('../../../../app/scripts/controllers/preferences')
describe('DetectTokensController', () => {
- const sandbox = sinon.createSandbox()
- let clock, keyringMemStore, network, preferences
- beforeEach(async () => {
- keyringMemStore = new ObservableStore({ isUnlocked: false})
- network = new NetworkController({ provider: { type: 'mainnet' }})
- preferences = new PreferencesController({ network })
- })
- after(() => {
- sandbox.restore()
+ const sandbox = sinon.createSandbox()
+ let clock, keyringMemStore, network, preferences, controller
+
+ const noop = () => {}
+
+ const networkControllerProviderConfig = {
+ getAccounts: noop,
+ }
+
+ beforeEach(async () => {
+
+
+ nock('https://api.infura.io')
+ .get(/.*/)
+ .reply(200)
+
+ keyringMemStore = new ObservableStore({ isUnlocked: false})
+ network = new NetworkController()
+ preferences = new PreferencesController({ network })
+ controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
+
+ network.initializeProvider(networkControllerProviderConfig)
+
+ })
+
+ after(() => {
+ sandbox.restore()
+ nock.cleanAll()
})
it('should poll on correct interval', async () => {
@@ -26,7 +46,10 @@ describe('DetectTokensController', () => {
it('should be called on every polling period', async () => {
clock = sandbox.useFakeTimers()
+ const network = new NetworkController()
+ network.initializeProvider(networkControllerProviderConfig)
network.setProviderType('mainnet')
+ const preferences = new PreferencesController({ network })
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -44,8 +67,6 @@ describe('DetectTokensController', () => {
})
it('should not check tokens while in test network', async () => {
- network.setProviderType('rinkeby')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -58,7 +79,6 @@ describe('DetectTokensController', () => {
})
it('should only check and add tokens while in main network', async () => {
- network.setProviderType('mainnet')
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
@@ -75,7 +95,6 @@ describe('DetectTokensController', () => {
})
it('should not detect same token while in main network', async () => {
- network.setProviderType('mainnet')
preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8)
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
@@ -93,8 +112,6 @@ describe('DetectTokensController', () => {
})
it('should trigger detect new tokens when change address', async () => {
- network.setProviderType('mainnet')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = true
var stub = sandbox.stub(controller, 'detectNewTokens')
@@ -103,8 +120,6 @@ describe('DetectTokensController', () => {
})
it('should trigger detect new tokens when submit password', async () => {
- network.setProviderType('mainnet')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.selectedAddress = '0x0'
var stub = sandbox.stub(controller, 'detectNewTokens')
@@ -113,8 +128,6 @@ describe('DetectTokensController', () => {
})
it('should not trigger detect new tokens when not open or not unlocked', async () => {
- network.setProviderType('mainnet')
- const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true
controller.isUnlocked = false
var stub = sandbox.stub(controller, 'detectTokenBalance')
diff --git a/test/unit/app/controllers/metamask-controller-test.js b/test/unit/app/controllers/metamask-controller-test.js
index 9f25cf376..17be2c028 100644
--- a/test/unit/app/controllers/metamask-controller-test.js
+++ b/test/unit/app/controllers/metamask-controller-test.js
@@ -3,16 +3,22 @@ const sinon = require('sinon')
const clone = require('clone')
const nock = require('nock')
const createThoughStream = require('through2').obj
-const MetaMaskController = require('../../../../app/scripts/metamask-controller')
const blacklistJSON = require('eth-phishing-detect/src/config')
-const firstTimeState = require('../../../../app/scripts/first-time-state')
+const MetaMaskController = require('../../../../app/scripts/metamask-controller')
+const firstTimeState = require('../../../unit/localhostState')
+const createTxMeta = require('../../../lib/createTxMeta')
+const EthQuery = require('eth-query')
const currentNetworkId = 42
const DEFAULT_LABEL = 'Account 1'
+const DEFAULT_LABEL_2 = 'Account 2'
const TEST_SEED = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
const TEST_ADDRESS = '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'
+const TEST_ADDRESS_2 = '0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b'
+const TEST_ADDRESS_3 = '0xeb9e64b93097bc15f01f13eae97015c57ab64823'
const TEST_SEED_ALT = 'setup olympic issue mobile velvet surge alcohol burger horse view reopen gentle'
const TEST_ADDRESS_ALT = '0xc42edfcc21ed14dda456aa0756c153f7985d8813'
+const CUSTOM_RPC_URL = 'http://localhost:8545'
describe('MetaMaskController', function () {
let metamaskController
@@ -110,7 +116,7 @@ describe('MetaMaskController', function () {
}
const gasPrice = metamaskController.getGasPrice()
- assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price')
+ assert.equal(gasPrice, '0x174876e800', 'accurately estimates 65th percentile accepted gas price')
metamaskController.recentBlocksController = realRecentBlocksController
})
@@ -134,6 +140,9 @@ describe('MetaMaskController', function () {
describe('#createNewVaultAndRestore', function () {
it('should be able to call newVaultAndRestore despite a mistake.', async function () {
const password = 'what-what-what'
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
+
await metamaskController.createNewVaultAndRestore(password, TEST_SEED.slice(0, -1)).catch((e) => null)
await metamaskController.createNewVaultAndRestore(password, TEST_SEED)
@@ -141,6 +150,9 @@ describe('MetaMaskController', function () {
})
it('should clear previous identities after vault restoration', async () => {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
+
await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED)
assert.deepEqual(metamaskController.getState().identities, {
[TEST_ADDRESS]: { address: TEST_ADDRESS, name: DEFAULT_LABEL },
@@ -156,6 +168,54 @@ describe('MetaMaskController', function () {
[TEST_ADDRESS_ALT]: { address: TEST_ADDRESS_ALT, name: DEFAULT_LABEL },
})
})
+
+ it('should restore any consecutive accounts with balances', async () => {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.withArgs(TEST_ADDRESS).callsFake(() => {
+ return Promise.resolve('0x14ced5122ce0a000')
+ })
+ metamaskController.getBalance.withArgs(TEST_ADDRESS_2).callsFake(() => {
+ return Promise.resolve('0x0')
+ })
+ metamaskController.getBalance.withArgs(TEST_ADDRESS_3).callsFake(() => {
+ return Promise.resolve('0x14ced5122ce0a000')
+ })
+
+ await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED)
+ assert.deepEqual(metamaskController.getState().identities, {
+ [TEST_ADDRESS]: { address: TEST_ADDRESS, name: DEFAULT_LABEL },
+ [TEST_ADDRESS_2]: { address: TEST_ADDRESS_2, name: DEFAULT_LABEL_2 },
+ })
+ })
+ })
+
+ describe('#getBalance', () => {
+ it('should return the balance known by accountTracker', async () => {
+ const accounts = {}
+ const balance = '0x14ced5122ce0a000'
+ accounts[TEST_ADDRESS] = { balance: balance }
+
+ metamaskController.accountTracker.store.putState({ accounts: accounts })
+
+ const gotten = await metamaskController.getBalance(TEST_ADDRESS)
+
+ assert.equal(balance, gotten)
+ })
+
+ it('should ask the network for a balance when not known by accountTracker', async () => {
+ const accounts = {}
+ const balance = '0x14ced5122ce0a000'
+ const ethQuery = new EthQuery()
+ sinon.stub(ethQuery, 'getBalance').callsFake((account, callback) => {
+ callback(undefined, balance)
+ })
+
+ metamaskController.accountTracker.store.putState({ accounts: accounts })
+
+ const gotten = await metamaskController.getBalance(TEST_ADDRESS, ethQuery)
+
+ assert.equal(balance, gotten)
+ })
})
describe('#getApi', function () {
@@ -360,29 +420,19 @@ describe('MetaMaskController', function () {
})
describe('#setCustomRpc', function () {
- const customRPC = 'https://custom.rpc/'
let rpcTarget
beforeEach(function () {
-
- nock('https://custom.rpc')
- .post('/')
- .reply(200)
-
- rpcTarget = metamaskController.setCustomRpc(customRPC)
- })
-
- afterEach(function () {
- nock.cleanAll()
+ rpcTarget = metamaskController.setCustomRpc(CUSTOM_RPC_URL)
})
it('returns custom RPC that when called', async function () {
- assert.equal(await rpcTarget, customRPC)
+ assert.equal(await rpcTarget, CUSTOM_RPC_URL)
})
it('changes the network controller rpc', function () {
const networkControllerState = metamaskController.networkController.store.getState()
- assert.equal(networkControllerState.provider.rpcTarget, customRPC)
+ assert.equal(networkControllerState.provider.rpcTarget, CUSTOM_RPC_URL)
})
})
@@ -487,9 +537,10 @@ describe('MetaMaskController', function () {
getNetworkstub.returns(42)
metamaskController.txController.txStateManager._saveTxList([
- { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'} },
- { id: 2, status: 'rejected', metamaskNetworkId: 32, txParams: {} },
- { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4'} },
+ createTxMeta({ id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'} }),
+ createTxMeta({ id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'} }),
+ createTxMeta({ id: 2, status: 'rejected', metamaskNetworkId: 32 }),
+ createTxMeta({ id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4'} }),
])
})
@@ -522,7 +573,7 @@ describe('MetaMaskController', function () {
assert(metamaskController.preferencesController.removeAddress.calledWith(addressToRemove))
})
it('should call accountTracker.removeAccount', async function () {
- assert(metamaskController.accountTracker.removeAccount.calledWith(addressToRemove))
+ assert(metamaskController.accountTracker.removeAccount.calledWith([addressToRemove]))
})
it('should call keyringController.removeAccount', async function () {
assert(metamaskController.keyringController.removeAccount.calledWith(addressToRemove))
@@ -533,22 +584,18 @@ describe('MetaMaskController', function () {
})
describe('#clearSeedWordCache', function () {
+ it('should set seed words to null', function (done) {
+ sandbox.stub(metamaskController.preferencesController, 'setSeedWords')
+ metamaskController.clearSeedWordCache((err) => {
+ if (err) {
+ done(err)
+ }
- it('should have set seed words', function () {
- metamaskController.configManager.setSeedWords('test words')
- const getConfigSeed = metamaskController.configManager.getSeedWords()
- assert.equal(getConfigSeed, 'test words')
- })
-
- it('should clear config seed phrase', function () {
- metamaskController.configManager.setSeedWords('test words')
- metamaskController.clearSeedWordCache((err, result) => {
- if (err) console.log(err)
+ assert.ok(metamaskController.preferencesController.setSeedWords.calledOnce)
+ assert.deepEqual(metamaskController.preferencesController.setSeedWords.args, [[null]])
+ done()
})
- const getConfigSeed = metamaskController.configManager.getSeedWords()
- assert.equal(getConfigSeed, null)
})
-
})
describe('#setCurrentLocale', function () {
@@ -566,14 +613,16 @@ describe('MetaMaskController', function () {
})
- describe('#newUnsignedMessage', function () {
+ describe('#newUnsignedMessage', () => {
let msgParams, metamaskMsgs, messages, msgId
const address = '0xc42edfcc21ed14dda456aa0756c153f7985d8813'
const data = '0x43727970746f6b697474696573'
- beforeEach(async function () {
+ beforeEach(async () => {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED_ALT)
@@ -582,7 +631,10 @@ describe('MetaMaskController', function () {
'data': data,
}
- metamaskController.newUnsignedMessage(msgParams, noop)
+ const promise = metamaskController.newUnsignedMessage(msgParams)
+ // handle the promise so it doesn't throw an unhandledRejection
+ promise.then(noop).catch(noop)
+
metamaskMsgs = metamaskController.messageManager.getUnapprovedMsgs()
messages = metamaskController.messageManager.messages
msgId = Object.keys(metamaskMsgs)[0]
@@ -622,13 +674,16 @@ describe('MetaMaskController', function () {
describe('#newUnsignedPersonalMessage', function () {
- it('errors with no from in msgParams', function () {
+ it('errors with no from in msgParams', async () => {
const msgParams = {
'data': data,
}
- metamaskController.newUnsignedPersonalMessage(msgParams, function (error) {
+ try {
+ await metamaskController.newUnsignedPersonalMessage(msgParams)
+ assert.fail('should have thrown')
+ } catch (error) {
assert.equal(error.message, 'MetaMask Message Signature: from field is required.')
- })
+ }
})
let msgParams, metamaskPersonalMsgs, personalMessages, msgId
@@ -637,6 +692,8 @@ describe('MetaMaskController', function () {
const data = '0x43727970746f6b697474696573'
beforeEach(async function () {
+ sandbox.stub(metamaskController, 'getBalance')
+ metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
await metamaskController.createNewVaultAndRestore('foobar1337', TEST_SEED_ALT)
@@ -645,7 +702,10 @@ describe('MetaMaskController', function () {
'data': data,
}
- metamaskController.newUnsignedPersonalMessage(msgParams, noop)
+ const promise = metamaskController.newUnsignedPersonalMessage(msgParams)
+ // handle the promise so it doesn't throw an unhandledRejection
+ promise.then(noop).catch(noop)
+
metamaskPersonalMsgs = metamaskController.personalMessageManager.getUnapprovedMsgs()
personalMessages = metamaskController.personalMessageManager.messages
msgId = Object.keys(metamaskPersonalMsgs)[0]
@@ -684,22 +744,27 @@ describe('MetaMaskController', function () {
describe('#setupUntrustedCommunication', function () {
let streamTest
- const phishingUrl = 'decentral.market'
+ const phishingUrl = 'myethereumwalletntw.com'
afterEach(function () {
streamTest.end()
})
- it('sets up phishing stream for untrusted communication ', async function () {
+ it('sets up phishing stream for untrusted communication ', async () => {
await metamaskController.blacklistController.updatePhishingList()
+ console.log(blacklistJSON.blacklist.includes(phishingUrl))
+
+ const { promise, resolve } = deferredPromise()
streamTest = createThoughStream((chunk, enc, cb) => {
- assert.equal(chunk.name, 'phishing')
+ if (chunk.name !== 'phishing') return cb()
assert.equal(chunk.data.hostname, phishingUrl)
- cb()
- })
- // console.log(streamTest)
- metamaskController.setupUntrustedCommunication(streamTest, phishingUrl)
+ resolve()
+ cb()
+ })
+ metamaskController.setupUntrustedCommunication(streamTest, phishingUrl)
+
+ await promise
})
})
@@ -724,25 +789,102 @@ describe('MetaMaskController', function () {
describe('#markAccountsFound', function () {
it('adds lost accounts to config manager data', function () {
metamaskController.markAccountsFound(noop)
- const configManagerData = metamaskController.configManager.getData()
- assert.deepEqual(configManagerData.lostAccounts, [])
+ const state = metamaskController.getState()
+ assert.deepEqual(state.lostAccounts, [])
})
})
describe('#markPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to true', function () {
metamaskController.markPasswordForgotten(noop)
- const configManagerData = metamaskController.configManager.getData()
- assert.equal(configManagerData.forgottenPassword, true)
+ const state = metamaskController.getState()
+ assert.equal(state.forgottenPassword, true)
})
})
describe('#unMarkPasswordForgotten', function () {
it('adds and sets forgottenPassword to config data to false', function () {
metamaskController.unMarkPasswordForgotten(noop)
- const configManagerData = metamaskController.configManager.getData()
- assert.equal(configManagerData.forgottenPassword, false)
+ const state = metamaskController.getState()
+ assert.equal(state.forgottenPassword, false)
+ })
+ })
+
+ describe('#_onKeyringControllerUpdate', function () {
+ it('should do nothing if there are no keyrings in state', async function () {
+ const addAddresses = sinon.fake()
+ const syncWithAddresses = sinon.fake()
+ sandbox.replace(metamaskController, 'preferencesController', {
+ addAddresses,
+ })
+ sandbox.replace(metamaskController, 'accountTracker', {
+ syncWithAddresses,
+ })
+
+ const oldState = metamaskController.getState()
+ await metamaskController._onKeyringControllerUpdate({keyrings: []})
+
+ assert.ok(addAddresses.notCalled)
+ assert.ok(syncWithAddresses.notCalled)
+ assert.deepEqual(metamaskController.getState(), oldState)
+ })
+
+ it('should update selected address if keyrings was locked', async function () {
+ const addAddresses = sinon.fake()
+ const getSelectedAddress = sinon.fake.returns('0x42')
+ const setSelectedAddress = sinon.fake()
+ const syncWithAddresses = sinon.fake()
+ sandbox.replace(metamaskController, 'preferencesController', {
+ addAddresses,
+ getSelectedAddress,
+ setSelectedAddress,
+ })
+ sandbox.replace(metamaskController, 'accountTracker', {
+ syncWithAddresses,
+ })
+
+ const oldState = metamaskController.getState()
+ await metamaskController._onKeyringControllerUpdate({
+ isUnlocked: false,
+ keyrings: [{
+ accounts: ['0x1', '0x2'],
+ }],
+ })
+
+ assert.deepEqual(addAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(syncWithAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(setSelectedAddress.args, [['0x1']])
+ assert.deepEqual(metamaskController.getState(), oldState)
+ })
+
+ it('should NOT update selected address if already unlocked', async function () {
+ const addAddresses = sinon.fake()
+ const syncWithAddresses = sinon.fake()
+ sandbox.replace(metamaskController, 'preferencesController', {
+ addAddresses,
+ })
+ sandbox.replace(metamaskController, 'accountTracker', {
+ syncWithAddresses,
+ })
+
+ const oldState = metamaskController.getState()
+ await metamaskController._onKeyringControllerUpdate({
+ isUnlocked: true,
+ keyrings: [{
+ accounts: ['0x1', '0x2'],
+ }],
+ })
+
+ assert.deepEqual(addAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(syncWithAddresses.args, [[['0x1', '0x2']]])
+ assert.deepEqual(metamaskController.getState(), oldState)
})
})
})
+
+function deferredPromise () {
+ let resolve
+ const promise = new Promise(_resolve => { resolve = _resolve })
+ return { promise, resolve }
+}
diff --git a/test/unit/app/controllers/network-contoller-test.js b/test/unit/app/controllers/network-contoller-test.js
index e16fb104e..822311931 100644
--- a/test/unit/app/controllers/network-contoller-test.js
+++ b/test/unit/app/controllers/network-contoller-test.js
@@ -32,9 +32,10 @@ describe('# Network Controller', function () {
describe('#provider', function () {
it('provider should be updatable without reassignment', function () {
networkController.initializeProvider(networkControllerProviderConfig)
- const proxy = networkController._proxy
- proxy.setTarget({ test: true, on: () => {} })
- assert.ok(proxy.test)
+ const providerProxy = networkController.getProviderAndBlockTracker().provider
+ assert.equal(providerProxy.test, undefined)
+ providerProxy.setTarget({ test: true })
+ assert.equal(providerProxy.test, true)
})
})
describe('#getNetworkState', function () {
diff --git a/test/unit/app/controllers/notice-controller-test.js b/test/unit/app/controllers/notice-controller-test.js
index b3ae75080..834c88f7b 100644
--- a/test/unit/app/controllers/notice-controller-test.js
+++ b/test/unit/app/controllers/notice-controller-test.js
@@ -1,16 +1,11 @@
const assert = require('assert')
-const configManagerGen = require('../../../lib/mock-config-manager')
const NoticeController = require('../../../../app/scripts/notice-controller')
describe('notice-controller', function () {
var noticeController
beforeEach(function () {
- // simple localStorage polyfill
- const configManager = configManagerGen()
- noticeController = new NoticeController({
- configManager: configManager,
- })
+ noticeController = new NoticeController()
})
describe('notices', function () {
diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js
index 9b2c846bd..b5ccf3fb5 100644
--- a/test/unit/app/controllers/preferences-controller-test.js
+++ b/test/unit/app/controllers/preferences-controller-test.js
@@ -1,6 +1,7 @@
const assert = require('assert')
const ObservableStore = require('obs-store')
const PreferencesController = require('../../../../app/scripts/controllers/preferences')
+const sinon = require('sinon')
describe('preferences controller', function () {
let preferencesController
@@ -339,5 +340,144 @@ describe('preferences controller', function () {
assert.deepEqual(tokensSecond, initialTokensSecond, 'tokens equal for same network')
})
})
+
+ describe('on watchAsset', function () {
+ var stubNext, stubEnd, stubHandleWatchAssetERC20, asy, req, res
+ const sandbox = sinon.createSandbox()
+
+ beforeEach(() => {
+ req = {params: {}}
+ res = {}
+ asy = {next: () => {}, end: () => {}}
+ stubNext = sandbox.stub(asy, 'next')
+ stubEnd = sandbox.stub(asy, 'end').returns(0)
+ stubHandleWatchAssetERC20 = sandbox.stub(preferencesController, '_handleWatchAssetERC20')
+ })
+ after(() => {
+ sandbox.restore()
+ })
+
+ it('shouldn not do anything if method not corresponds', async function () {
+ const asy = {next: () => {}, end: () => {}}
+ var stubNext = sandbox.stub(asy, 'next')
+ var stubEnd = sandbox.stub(asy, 'end').returns(0)
+ req.method = 'metamask'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.notCalled(stubEnd)
+ sandbox.assert.called(stubNext)
+ })
+ it('should do something if method is supported', async function () {
+ const asy = {next: () => {}, end: () => {}}
+ var stubNext = sandbox.stub(asy, 'next')
+ var stubEnd = sandbox.stub(asy, 'end').returns(0)
+ req.method = 'metamask_watchAsset'
+ req.params.type = 'someasset'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.called(stubEnd)
+ sandbox.assert.notCalled(stubNext)
+ })
+ it('should through error if method is supported but asset type is not', async function () {
+ req.method = 'metamask_watchAsset'
+ req.params.type = 'someasset'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.called(stubEnd)
+ sandbox.assert.notCalled(stubHandleWatchAssetERC20)
+ sandbox.assert.notCalled(stubNext)
+ assert.deepEqual(res, {})
+ })
+ it('should trigger handle add asset if type supported', async function () {
+ const asy = {next: () => {}, end: () => {}}
+ req.method = 'metamask_watchAsset'
+ req.params.type = 'ERC20'
+ await preferencesController.requestWatchAsset(req, res, asy.next, asy.end)
+ sandbox.assert.called(stubHandleWatchAssetERC20)
+ })
+ })
+
+ describe('on watchAsset of type ERC20', function () {
+ var req
+
+ const sandbox = sinon.createSandbox()
+ beforeEach(() => {
+ req = {params: {type: 'ERC20'}}
+ })
+ after(() => {
+ sandbox.restore()
+ })
+
+ it('should add suggested token', async function () {
+ const address = '0xabcdef1234567'
+ const symbol = 'ABBR'
+ const decimals = 5
+ const image = 'someimage'
+ req.params.options = { address, symbol, decimals, image }
+
+ sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true)
+ preferencesController.showWatchAssetUi = async () => {}
+
+ await preferencesController._handleWatchAssetERC20(req.params.options)
+ const suggested = preferencesController.getSuggestedTokens()
+ assert.equal(Object.keys(suggested).length, 1, `one token added ${Object.keys(suggested)}`)
+
+ assert.equal(suggested[address].address, address, 'set address correctly')
+ assert.equal(suggested[address].symbol, symbol, 'set symbol correctly')
+ assert.equal(suggested[address].decimals, decimals, 'set decimals correctly')
+ assert.equal(suggested[address].image, image, 'set image correctly')
+ })
+
+ it('should add token correctly if user confirms', async function () {
+ const address = '0xabcdef1234567'
+ const symbol = 'ABBR'
+ const decimals = 5
+ const image = 'someimage'
+ req.params.options = { address, symbol, decimals, image }
+
+ sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true)
+ preferencesController.showWatchAssetUi = async () => {
+ await preferencesController.addToken(address, symbol, decimals, image)
+ }
+
+ await preferencesController._handleWatchAssetERC20(req.params.options)
+ const tokens = preferencesController.getTokens()
+ assert.equal(tokens.length, 1, `one token added`)
+ const added = tokens[0]
+ assert.equal(added.address, address, 'set address correctly')
+ assert.equal(added.symbol, symbol, 'set symbol correctly')
+ assert.equal(added.decimals, decimals, 'set decimals correctly')
+
+ const assetImages = preferencesController.getAssetImages()
+ assert.ok(assetImages[address], `set image correctly`)
+ })
+ })
+
+ describe('setPasswordForgotten', function () {
+ it('should default to false', function () {
+ const state = preferencesController.store.getState()
+ assert.equal(state.forgottenPassword, false)
+ })
+
+ it('should set the forgottenPassword property in state', function () {
+ assert.equal(preferencesController.store.getState().forgottenPassword, false)
+
+ preferencesController.setPasswordForgotten(true)
+
+ assert.equal(preferencesController.store.getState().forgottenPassword, true)
+ })
+ })
+
+ describe('setSeedWords', function () {
+ it('should default to null', function () {
+ const state = preferencesController.store.getState()
+ assert.equal(state.seedWords, null)
+ })
+
+ it('should set the seedWords property in state', function () {
+ assert.equal(preferencesController.store.getState().seedWords, null)
+
+ preferencesController.setSeedWords('foo bar baz')
+
+ assert.equal(preferencesController.store.getState().seedWords, 'foo bar baz')
+ })
+ })
})
diff --git a/test/unit/app/controllers/transactions/nonce-tracker-test.js b/test/unit/app/controllers/transactions/nonce-tracker-test.js
index 6c0ac759f..51ac390e9 100644
--- a/test/unit/app/controllers/transactions/nonce-tracker-test.js
+++ b/test/unit/app/controllers/transactions/nonce-tracker-test.js
@@ -224,14 +224,15 @@ function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') {
providerResultStub.result = providerStub
const provider = {
sendAsync: (_, cb) => { cb(undefined, providerResultStub) },
- _blockTracker: {
- getCurrentBlock: () => '0x11b568',
- },
+ }
+ const blockTracker = {
+ getCurrentBlock: () => '0x11b568',
+ getLatestBlock: async () => '0x11b568',
}
return new NonceTracker({
provider,
+ blockTracker,
getPendingTransactions,
getConfirmedTransactions,
})
}
-
diff --git a/test/unit/app/controllers/transactions/pending-tx-test.js b/test/unit/app/controllers/transactions/pending-tx-test.js
index 8bf2da6f8..ba15f1953 100644
--- a/test/unit/app/controllers/transactions/pending-tx-test.js
+++ b/test/unit/app/controllers/transactions/pending-tx-test.js
@@ -9,6 +9,7 @@ describe('PendingTransactionTracker', function () {
let pendingTxTracker, txMeta, txMetaNoHash, providerResultStub,
provider, txMeta3, txList, knownErrors
this.timeout(10000)
+
beforeEach(function () {
txMeta = {
id: 1,
@@ -40,7 +41,10 @@ describe('PendingTransactionTracker', function () {
getPendingTransactions: () => { return [] },
getCompletedTransactions: () => { return [] },
publishTransaction: () => {},
+ confirmTransaction: () => {},
})
+
+ pendingTxTracker._getBlock = (blockNumber) => { return {number: blockNumber, transactions: []} }
})
describe('_checkPendingTx state management', function () {
@@ -92,58 +96,6 @@ describe('PendingTransactionTracker', function () {
})
})
- describe('#checkForTxInBlock', function () {
- it('should return if no pending transactions', function () {
- // throw a type error if it trys to do anything on the block
- // thus failing the test
- const block = Proxy.revocable({}, {}).revoke()
- pendingTxTracker.checkForTxInBlock(block)
- })
- it('should emit \'tx:failed\' if the txMeta does not have a hash', function (done) {
- const block = Proxy.revocable({}, {}).revoke()
- pendingTxTracker.getPendingTransactions = () => [txMetaNoHash]
- pendingTxTracker.once('tx:failed', (txId, err) => {
- assert(txId, txMetaNoHash.id, 'should pass txId')
- done()
- })
- pendingTxTracker.checkForTxInBlock(block)
- })
- it('should emit \'txConfirmed\' if the tx is in the block', function (done) {
- const block = { transactions: [txMeta]}
- pendingTxTracker.getPendingTransactions = () => [txMeta]
- pendingTxTracker.once('tx:confirmed', (txId) => {
- assert(txId, txMeta.id, 'should pass txId')
- done()
- })
- pendingTxTracker.once('tx:failed', (_, err) => { done(err) })
- pendingTxTracker.checkForTxInBlock(block)
- })
- })
- describe('#queryPendingTxs', function () {
- it('should call #_checkPendingTxs if their is no oldBlock', function (done) {
- let oldBlock
- const newBlock = { number: '0x01' }
- pendingTxTracker._checkPendingTxs = done
- pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
- })
- it('should call #_checkPendingTxs if oldBlock and the newBlock have a diff of greater then 1', function (done) {
- const oldBlock = { number: '0x01' }
- const newBlock = { number: '0x03' }
- pendingTxTracker._checkPendingTxs = done
- pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
- })
- it('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less', function (done) {
- const oldBlock = { number: '0x1' }
- const newBlock = { number: '0x2' }
- pendingTxTracker._checkPendingTxs = () => {
- const err = new Error('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less')
- done(err)
- }
- pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
- done()
- })
- })
-
describe('#_checkPendingTx', function () {
it('should emit \'tx:failed\' if the txMeta does not have a hash', function (done) {
pendingTxTracker.once('tx:failed', (txId, err) => {
@@ -157,16 +109,6 @@ describe('PendingTransactionTracker', function () {
providerResultStub.eth_getTransactionByHash = null
pendingTxTracker._checkPendingTx(txMeta)
})
-
- it('should emit \'txConfirmed\'', function (done) {
- providerResultStub.eth_getTransactionByHash = {blockNumber: '0x01'}
- pendingTxTracker.once('tx:confirmed', (txId) => {
- assert(txId, txMeta.id, 'should pass txId')
- done()
- })
- pendingTxTracker.once('tx:failed', (_, err) => { done(err) })
- pendingTxTracker._checkPendingTx(txMeta)
- })
})
describe('#_checkPendingTxs', function () {
@@ -180,19 +122,19 @@ describe('PendingTransactionTracker', function () {
})
})
- it('should warp all txMeta\'s in #_checkPendingTx', function (done) {
+ it('should warp all txMeta\'s in #updatePendingTxs', function (done) {
pendingTxTracker.getPendingTransactions = () => txList
pendingTxTracker._checkPendingTx = (tx) => { tx.resolve(tx) }
Promise.all(txList.map((tx) => tx.processed))
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker._checkPendingTxs()
+ pendingTxTracker.updatePendingTxs()
})
})
describe('#resubmitPendingTxs', function () {
- const blockStub = { number: '0x0' }
+ const blockNumberStub = '0x0'
beforeEach(function () {
const txMeta2 = txMeta3 = txMeta
txList = [txMeta, txMeta2, txMeta3].map((tx) => {
@@ -210,7 +152,7 @@ describe('PendingTransactionTracker', function () {
Promise.all(txList.map((tx) => tx.processed))
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker.resubmitPendingTxs(blockStub)
+ pendingTxTracker.resubmitPendingTxs(blockNumberStub)
})
it('should not emit \'tx:failed\' if the txMeta throws a known txError', function (done) {
knownErrors = [
@@ -237,7 +179,7 @@ describe('PendingTransactionTracker', function () {
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker.resubmitPendingTxs(blockStub)
+ pendingTxTracker.resubmitPendingTxs(blockNumberStub)
})
it('should emit \'tx:warning\' if it encountered a real error', function (done) {
pendingTxTracker.once('tx:warning', (txMeta, err) => {
@@ -255,7 +197,7 @@ describe('PendingTransactionTracker', function () {
.then((txCompletedList) => done())
.catch(done)
- pendingTxTracker.resubmitPendingTxs(blockStub)
+ pendingTxTracker.resubmitPendingTxs(blockNumberStub)
})
})
describe('#_resubmitTx', function () {
diff --git a/test/unit/app/controllers/transactions/tx-controller-test.js b/test/unit/app/controllers/transactions/tx-controller-test.js
index 26dc7b656..ea58aa560 100644
--- a/test/unit/app/controllers/transactions/tx-controller-test.js
+++ b/test/unit/app/controllers/transactions/tx-controller-test.js
@@ -1,4 +1,5 @@
const assert = require('assert')
+const EventEmitter = require('events')
const ethUtil = require('ethereumjs-util')
const EthTx = require('ethereumjs-tx')
const ObservableStore = require('obs-store')
@@ -22,12 +23,14 @@ describe('Transaction Controller', function () {
}
provider = createTestProviderTools({ scaffold: providerResultStub }).provider
fromAccount = getTestAccounts()[0]
-
+ const blockTrackerStub = new EventEmitter()
+ blockTrackerStub.getCurrentBlock = noop
+ blockTrackerStub.getLatestBlock = noop
txController = new TransactionController({
provider,
networkStore: new ObservableStore(currentNetworkId),
txHistoryLimit: 10,
- blockTracker: { getCurrentBlock: noop, on: noop, once: noop },
+ blockTracker: blockTrackerStub,
signTransaction: (ethTx) => new Promise((resolve) => {
ethTx.sign(fromAccount.key)
resolve()
@@ -49,9 +52,9 @@ describe('Transaction Controller', function () {
describe('#getUnapprovedTxCount', function () {
it('should return the number of unapproved txs', function () {
txController.txStateManager._saveTxList([
- { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 2, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 2, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
])
const unapprovedTxCount = txController.getUnapprovedTxCount()
assert.equal(unapprovedTxCount, 3, 'should be 3')
@@ -61,9 +64,9 @@ describe('Transaction Controller', function () {
describe('#getPendingTxCount', function () {
it('should return the number of pending txs', function () {
txController.txStateManager._saveTxList([
- { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
+ { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {}, history: [] },
])
const pendingTxCount = txController.getPendingTxCount()
assert.equal(pendingTxCount, 3, 'should be 3')
@@ -79,15 +82,15 @@ describe('Transaction Controller', function () {
'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
}
txController.txStateManager._saveTxList([
- {id: 0, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 2, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams},
- {id: 4, status: 'rejected', metamaskNetworkId: currentNetworkId, txParams},
- {id: 5, status: 'approved', metamaskNetworkId: currentNetworkId, txParams},
- {id: 6, status: 'signed', metamaskNetworkId: currentNetworkId, txParams},
- {id: 7, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams},
- {id: 8, status: 'failed', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 0, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 2, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 4, status: 'rejected', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 5, status: 'approved', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 6, status: 'signed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 7, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [] },
+ {id: 8, status: 'failed', metamaskNetworkId: currentNetworkId, txParams, history: [] },
])
})
@@ -155,9 +158,19 @@ describe('Transaction Controller', function () {
})
describe('#addUnapprovedTransaction', function () {
+ const selectedAddress = '0x1678a085c290ebd122dc42cba69373b5953b831d'
+
+ let getSelectedAddress
+ beforeEach(function () {
+ getSelectedAddress = sinon.stub(txController, 'getSelectedAddress').returns(selectedAddress)
+ })
+
+ afterEach(function () {
+ getSelectedAddress.restore()
+ })
it('should add an unapproved transaction and return a valid txMeta', function (done) {
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
+ txController.addUnapprovedTransaction({ from: selectedAddress })
.then((txMeta) => {
assert(('id' in txMeta), 'should have a id')
assert(('time' in txMeta), 'should have a time stamp')
@@ -177,48 +190,58 @@ describe('Transaction Controller', function () {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d' })
+ txController.addUnapprovedTransaction({ from: selectedAddress })
.catch(done)
})
it('should fail if recipient is public', function (done) {
txController.networkStore = new ObservableStore(1)
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
+ txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch((err) => {
if (err.message === 'Recipient is a public account') done()
else done(err)
})
})
+ it('should fail if the from address isn\'t the selected address', function (done) {
+ txController.addUnapprovedTransaction({from: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2'})
+ .then(function () {
+ assert.fail('transaction should not have been added')
+ done()
+ })
+ .catch(function () {
+ assert.ok('pass')
+ done()
+ })
+ })
+
it('should not fail if recipient is public but not on mainnet', function (done) {
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
assert(txMetaFromEmit, 'txMeta is falsey')
done()
})
- txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
+ txController.addUnapprovedTransaction({ from: selectedAddress, to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
.catch(done)
})
})
describe('#addTxGasDefaults', function () {
- it('should add the tx defaults if their are none', function (done) {
+ it('should add the tx defaults if their are none', async () => {
const txMeta = {
- 'txParams': {
- 'from': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
- 'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ txParams: {
+ from: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ to: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
},
+ history: [],
}
- providerResultStub.eth_gasPrice = '4a817c800'
- providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' }
- providerResultStub.eth_estimateGas = '5209'
- txController.addTxGasDefaults(txMeta)
- .then((txMetaWithDefaults) => {
- assert(txMetaWithDefaults.txParams.value, '0x0', 'should have added 0x0 as the value')
- assert(txMetaWithDefaults.txParams.gasPrice, 'should have added the gas price')
- assert(txMetaWithDefaults.txParams.gas, 'should have added the gas field')
- done()
- })
- .catch(done)
+ providerResultStub.eth_gasPrice = '4a817c800'
+ providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' }
+ providerResultStub.eth_estimateGas = '5209'
+
+ const txMetaWithDefaults = await txController.addTxGasDefaults(txMeta)
+ assert(txMetaWithDefaults.txParams.value, '0x0', 'should have added 0x0 as the value')
+ assert(txMetaWithDefaults.txParams.gasPrice, 'should have added the gas price')
+ assert(txMetaWithDefaults.txParams.gas, 'should have added the gas field')
})
})
@@ -381,8 +404,9 @@ describe('Transaction Controller', function () {
})
it('should publish a tx, updates the rawTx when provided a one', async function () {
+ const rawTx = '0x477b2e6553c917af0db0388ae3da62965ff1a184558f61b749d1266b2e6d024c'
txController.txStateManager.addTx(txMeta)
- await txController.publishTransaction(txMeta.id)
+ await txController.publishTransaction(txMeta.id, rawTx)
const publishedTx = txController.txStateManager.getTx(1)
assert.equal(publishedTx.hash, hash)
assert.equal(publishedTx.status, 'submitted')
@@ -398,7 +422,7 @@ describe('Transaction Controller', function () {
data: '0x0',
}
txController.txStateManager._saveTxList([
- { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams },
+ { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams, history: [] },
])
txController.retryTransaction(1)
.then((txMeta) => {
diff --git a/test/unit/components/balance-component-test.js b/test/unit/components/balance-component-test.js
index 81e6fdf9e..aa9763b72 100644
--- a/test/unit/components/balance-component-test.js
+++ b/test/unit/components/balance-component-test.js
@@ -1,7 +1,7 @@
const assert = require('assert')
const h = require('react-hyperscript')
const { createMockStore } = require('redux-test-utils')
-const { shallowWithStore } = require('../../lib/shallow-with-store')
+const { shallowWithStore } = require('../../lib/render-helpers')
const BalanceComponent = require('../../../ui/app/components/balance-component')
const mockState = {
metamask: {
@@ -42,4 +42,3 @@ describe('BalanceComponent', function () {
})
})
-
diff --git a/test/unit/components/binary-renderer-test.js b/test/unit/components/binary-renderer-test.js
index 7bf9250cc..e428c26ad 100644
--- a/test/unit/components/binary-renderer-test.js
+++ b/test/unit/components/binary-renderer-test.js
@@ -4,7 +4,7 @@ var BinaryRenderer = require('../../../old-ui/app/components/binary-renderer')
describe('BinaryRenderer', function () {
let binaryRenderer
const message = 'Hello, world!'
- const buffer = new Buffer(message, 'utf8')
+ const buffer = Buffer.from(message, 'utf8')
const hex = buffer.toString('hex')
beforeEach(function () {
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
deleted file mode 100644
index b710e2dfb..000000000
--- a/test/unit/config-manager-test.js
+++ /dev/null
@@ -1,115 +0,0 @@
-// polyfill fetch
-global.fetch = global.fetch || require('isomorphic-fetch')
-
-const assert = require('assert')
-const configManagerGen = require('../lib/mock-config-manager')
-
-describe('config-manager', function () {
- var configManager
-
- beforeEach(function () {
- configManager = configManagerGen()
- })
-
- describe('#setConfig', function () {
- it('should set the config key', function () {
- var testConfig = {
- provider: {
- type: 'rpc',
- rpcTarget: 'foobar',
- },
- }
- configManager.setConfig(testConfig)
- var result = configManager.getData()
-
- assert.equal(result.config.provider.type, testConfig.provider.type)
- assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
- })
-
- it('setting wallet should not overwrite config', function () {
- var testConfig = {
- provider: {
- type: 'rpc',
- rpcTarget: 'foobar',
- },
- }
- configManager.setConfig(testConfig)
-
- var testWallet = {
- name: 'this is my fake wallet',
- }
- configManager.setWallet(testWallet)
-
- var result = configManager.getData()
- assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
- assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
-
- testConfig.provider.type = 'something else!'
- configManager.setConfig(testConfig)
-
- result = configManager.getData()
- assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
- assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
- assert.equal(result.config.provider.type, testConfig.provider.type)
- })
- })
-
- describe('wallet nicknames', function () {
- it('should return null when no nicknames are saved', function () {
- var nick = configManager.nicknameForWallet('0x0')
- assert.equal(nick, null, 'no nickname returned')
- })
-
- it('should persist nicknames', function () {
- var account = '0x0'
- var nick1 = 'foo'
- var nick2 = 'bar'
- configManager.setNicknameForWallet(account, nick1)
-
- var result1 = configManager.nicknameForWallet(account)
- assert.equal(result1, nick1)
-
- configManager.setNicknameForWallet(account, nick2)
- var result2 = configManager.nicknameForWallet(account)
- assert.equal(result2, nick2)
- })
- })
-
- describe('rpc manipulations', function () {
- it('changing rpc should return a different rpc', function () {
- var firstRpc = 'first'
- var secondRpc = 'second'
-
- configManager.setRpcTarget(firstRpc)
- var firstResult = configManager.getCurrentRpcAddress()
- assert.equal(firstResult, firstRpc)
-
- configManager.setRpcTarget(secondRpc)
- var secondResult = configManager.getCurrentRpcAddress()
- assert.equal(secondResult, secondRpc)
- })
- })
-
- describe('transactions', function () {
- beforeEach(function () {
- configManager.setTxList([])
- })
-
- describe('#getTxList', function () {
- it('when new should return empty array', function () {
- var result = configManager.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 0)
- })
- })
-
- describe('#setTxList', function () {
- it('saves the submitted data to the tx list', function () {
- var target = [{ foo: 'bar' }]
- configManager.setTxList(target)
- var result = configManager.getTxList()
- assert.equal(result[0].foo, 'bar')
- })
- })
- })
-})
diff --git a/test/unit/development/sample-changelog.md b/test/unit/development/sample-changelog.md
index 8fc9d2145..fd980e375 100644
--- a/test/unit/development/sample-changelog.md
+++ b/test/unit/development/sample-changelog.md
@@ -67,7 +67,7 @@
- Estimating gas limit for simple ether sends now faster & cheaper, by avoiding VM usage on recipients with no code.
- Add an extra px to address for Firefox clipping.
- Fix Firefox scrollbar.
-- Open metamask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation.
+- Open MetaMask popup for transaction confirmation before gas estimation finishes and add a loading screen over transaction confirmation.
- Fix bug that prevented eth_signTypedData from signing bytes.
- Further improve gas price estimation.
@@ -330,7 +330,7 @@ rollback to 3.10.0 due to bug
## 3.7.7 2017-6-8
-- Fix bug where metamask would show old data after computer being asleep or disconnected from the internet.
+- Fix bug where MetaMask would show old data after computer being asleep or disconnected from the internet.
## 3.7.6 2017-6-5
diff --git a/test/unit/localhostState.js b/test/unit/localhostState.js
new file mode 100644
index 000000000..f9fa157d7
--- /dev/null
+++ b/test/unit/localhostState.js
@@ -0,0 +1,21 @@
+
+/**
+ * @typedef {Object} FirstTimeState
+ * @property {Object} config Initial configuration parameters
+ * @property {Object} NetworkController Network controller state
+ */
+
+/**
+ * @type {FirstTimeState}
+ */
+const initialState = {
+ config: {},
+ NetworkController: {
+ provider: {
+ type: 'rpc',
+ rpcTarget: 'http://localhost:8545',
+ },
+ },
+}
+
+module.exports = initialState
diff --git a/test/unit/responsive/components/dropdown-test.js b/test/unit/responsive/components/dropdown-test.js
index 493b01918..f3f236d90 100644
--- a/test/unit/responsive/components/dropdown-test.js
+++ b/test/unit/responsive/components/dropdown-test.js
@@ -6,7 +6,7 @@ const path = require('path')
const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown
const { createMockStore } = require('redux-test-utils')
-const { mountWithStore } = require('../../../lib/shallow-with-store')
+const { mountWithStore } = require('../../../lib/render-helpers')
const mockState = {
metamask: {
diff --git a/test/unit/ui/add-token.spec.js b/test/unit/ui/add-token.spec.js
index 69b7fb620..f6b6155a0 100644
--- a/test/unit/ui/add-token.spec.js
+++ b/test/unit/ui/add-token.spec.js
@@ -1,7 +1,7 @@
const assert = require('assert')
const { createMockStore } = require('redux-test-utils')
const h = require('react-hyperscript')
-const { shallowWithStore } = require('../../lib/shallow-with-store')
+const { shallowWithStore } = require('../../lib/render-helpers')
const AddTokenScreen = require('../../../old-ui/app/add-token')
describe('Add Token Screen', function () {
diff --git a/test/unit/ui/app/actions.spec.js b/test/unit/ui/app/actions.spec.js
new file mode 100644
index 000000000..748a58b32
--- /dev/null
+++ b/test/unit/ui/app/actions.spec.js
@@ -0,0 +1,1468 @@
+// Used to inspect long objects
+// util.inspect({JSON}, false, null))
+// const util = require('util')
+const assert = require('assert')
+const sinon = require('sinon')
+const clone = require('clone')
+const nock = require('nock')
+const fetchMock = require('fetch-mock')
+const configureStore = require('redux-mock-store').default
+const thunk = require('redux-thunk').default
+const EthQuery = require('eth-query')
+const Eth = require('ethjs')
+const KeyringController = require('eth-keyring-controller')
+
+const { createTestProviderTools } = require('../../../stub/provider')
+const provider = createTestProviderTools({ scaffold: {}}).provider
+
+const enLocale = require('../../../../app/_locales/en/messages.json')
+const actions = require('../../../../ui/app/actions')
+const MetaMaskController = require('../../../../app/scripts/metamask-controller')
+
+const firstTimeState = require('../../../unit/localhostState')
+const devState = require('../../../data/2-state.json')
+
+const middleware = [thunk]
+const mockStore = configureStore(middleware)
+
+describe('Actions', () => {
+
+ const noop = () => {}
+
+ 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 (password, object) {
+ this.object = object
+ return Promise.resolve('mock-encrypted')
+ },
+ decrypt: function () {
+ return Promise.resolve(this.object)
+ },
+ },
+ initState: clone(firstTimeState),
+ })
+
+ 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('', async () => {
+
+ const store = mockStore({})
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ verifySeedPhraseSpy = sinon.spy(background, 'verifySeedPhrase')
+
+ return store.dispatch(actions.tryUnlockMetamask())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(verifySeedPhraseSpy.calledOnce)
+ })
+ })
+
+ it('errors on submitPassword will fail', () => {
+
+ 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((password, callback) => {
+ callback(new Error('error in submitPassword'))
+ })
+
+ return store.dispatch(actions.tryUnlockMetamask('test'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('displays warning error and unlock failed when verifySeed fails', () => {
+ 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'))
+ })
+
+ return store.dispatch(actions.tryUnlockMetamask('test'))
+ .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('#confirmSeedWords', () => {
+
+ let clearSeedWordCacheSpy
+
+ afterEach(() => {
+ clearSeedWordCacheSpy.restore()
+ })
+
+ it('shows account page after clearing seed word cache', () => {
+
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.spy(background, 'clearSeedWordCache')
+
+ return store.dispatch(actions.confirmSeedWords())
+ .then(() => {
+ assert.equal(clearSeedWordCacheSpy.callCount, 1)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors in callback will display warning', () => {
+ const store = mockStore({})
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.stub(background, 'clearSeedWordCache')
+
+ clearSeedWordCacheSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.confirmSeedWords())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#createNewVaultAndRestore', () => {
+
+ let createNewVaultAndRestoreSpy, clearSeedWordCacheSpy
+
+ afterEach(() => {
+ createNewVaultAndRestoreSpy.restore()
+ })
+
+ it('clears seed words and restores new vault', () => {
+
+ const store = mockStore({})
+
+ createNewVaultAndRestoreSpy = sinon.spy(background, 'createNewVaultAndRestore')
+ clearSeedWordCacheSpy = sinon.spy(background, 'clearSeedWordCache')
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert(clearSeedWordCacheSpy.calledOnce)
+ assert(createNewVaultAndRestoreSpy.calledOnce)
+ })
+ })
+
+ it('errors when callback in clearSeedWordCache throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ clearSeedWordCacheSpy = sinon.stub(background, 'clearSeedWordCache')
+ clearSeedWordCacheSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors when callback in createNewVaultAndRestore throws', () => {
+
+ 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((password, seed, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndRestore())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#createNewVaultAndKeychain', () => {
+
+ let createNewVaultAndKeychainSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ createNewVaultAndKeychainSpy.restore()
+ placeSeedWordsSpy.restore()
+ })
+
+ it('calls createNewVaultAndKeychain and placeSeedWords in background', () => {
+
+ const store = mockStore()
+
+ createNewVaultAndKeychainSpy = sinon.spy(background, 'createNewVaultAndKeychain')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert(createNewVaultAndKeychainSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays error and value when callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ createNewVaultAndKeychainSpy = sinon.stub(background, 'createNewVaultAndKeychain')
+ createNewVaultAndKeychainSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+
+ it('errors when placeSeedWords throws', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'HIDE_LOADING_INDICATION' },
+ ]
+
+ placeSeedWordsSpy = sinon.stub(background, 'placeSeedWords')
+ placeSeedWordsSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.createNewVaultAndKeychain())
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#requestRevealSeed', () => {
+
+ let submitPasswordSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ })
+
+ it('calls submitPassword and placeSeedWords from background', () => {
+
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.requestRevealSeed())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error with value when callback errors', () => {
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#requestRevealSeedWords', () => {
+ let submitPasswordSpy
+
+ it('calls submitPassword in background', () => {
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'verifySeedPhrase')
+
+ return store.dispatch(actions.requestRevealSeedWords())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message then callback in background errors', () => {
+ 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'))
+ })
+
+ return store.dispatch(actions.requestRevealSeedWords())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+ })
+
+ describe('#requestRevealSeed', () => {
+
+ let submitPasswordSpy, placeSeedWordsSpy
+
+ afterEach(() => {
+ submitPasswordSpy.restore()
+ placeSeedWordsSpy.restore()
+ })
+
+ it('calls submitPassword and placeSeedWords in background', () => {
+
+ const store = mockStore()
+
+ submitPasswordSpy = sinon.spy(background, 'submitPassword')
+ placeSeedWordsSpy = sinon.spy(background, 'placeSeedWords')
+
+ return store.dispatch(actions.requestRevealSeed())
+ .then(() => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(placeSeedWordsSpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message when submitPassword in background errors', () => {
+ submitPasswordSpy = sinon.stub(background, 'submitPassword')
+ submitPasswordSpy.callsFake((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('errors when placeSeedWords throw', () => {
+ placeSeedWordsSpy = sinon.stub(background, 'placeSeedWords')
+ placeSeedWordsSpy.callsFake((callback) => {
+ callback(new Error('error'))
+ })
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ return store.dispatch(actions.requestRevealSeed())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#removeAccount', () => {
+ let removeAccountSpy
+
+ afterEach(() => {
+ removeAccountSpy.restore()
+ })
+
+ it('calls removeAccount in background and expect actions to show account', () => {
+ const store = mockStore(devState)
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ removeAccountSpy = sinon.spy(background, 'removeAccount')
+
+ return store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
+ .then(() => {
+ assert(removeAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('displays warning error message when removeAccount callback errors', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ removeAccountSpy = sinon.stub(background, 'removeAccount')
+ removeAccountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.removeAccount('0xe18035bf8712672935fdb4e5e431b1a0183d2dfc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+ })
+
+ describe('#addNewKeyring', () => {
+ let addNewKeyringSpy
+
+ beforeEach(() => {
+ addNewKeyringSpy = sinon.stub(background, 'addNewKeyring')
+ })
+
+ afterEach(() => {
+ addNewKeyringSpy.restore()
+ })
+
+ it('', () => {
+ 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((type, opts, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.addNewKeyring())
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+
+ })
+
+ describe('#resetAccount', () => {
+
+ let resetAccountSpy
+
+ afterEach(() => {
+ resetAccountSpy.restore()
+ })
+
+ it('', () => {
+
+ const store = mockStore()
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SHOW_ACCOUNTS_PAGE' },
+ ]
+
+ resetAccountSpy = sinon.spy(background, 'resetAccount')
+
+ return store.dispatch(actions.resetAccount())
+ .then(() => {
+ assert(resetAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('', () => {
+ 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'))
+ })
+
+ return store.dispatch(actions.resetAccount())
+ .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'
+
+ return store.dispatch(actions.importNewAccount('Private Key', [ importPrivkey ]))
+ .then(() => {
+ assert(importAccountWithStrategySpy.calledOnce)
+ })
+ })
+
+ it('displays warning error message when importAccount in background callback errors', () => {
+ 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((strategy, args, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.importNewAccount())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#addNewAccount', () => {
+
+ let addNewAccountSpy
+
+ afterEach(() => {
+ addNewAccountSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore({ metamask: devState })
+
+ addNewAccountSpy = sinon.spy(background, 'addNewAccount')
+
+ return store.dispatch(actions.addNewAccount())
+ .then(() => {
+ assert(addNewAccountSpy.calledOnce)
+ })
+ })
+ })
+
+ describe('#setCurrentCurrency', () => {
+
+ let setCurrentCurrencySpy
+
+ beforeEach(() => {
+ setCurrentCurrencySpy = sinon.stub(background, 'setCurrentCurrency')
+ })
+
+ afterEach(() => {
+ setCurrentCurrencySpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.setCurrentCurrency('jpy'))
+ assert(setCurrentCurrencySpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setCurrentCurrencySpy.callsFake((currencyCode, 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')
+
+ return store.dispatch(actions.signMsg(msgParams))
+ .then(() => {
+ assert(signMessageSpy.calledOnce)
+ })
+
+ })
+
+ it('errors when signMessage in background throws', () => {
+ 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((msgData, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.signMsg())
+ .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('', () => {
+ const store = mockStore()
+
+ signPersonalMessageSpy = sinon.spy(background, 'signPersonalMessage')
+
+ return store.dispatch(actions.signPersonalMsg(msgParams))
+ .then(() => {
+ assert(signPersonalMessageSpy.calledOnce)
+ })
+
+ })
+
+ it('', () => {
+ 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((msgData, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.signPersonalMsg(msgParams))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ })
+
+ describe('#signTx', () => {
+
+ let sendTransactionSpy
+
+ beforeEach(() => {
+ global.ethQuery = new EthQuery(provider)
+ 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((txData, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.signTx())
+ 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('', () => {
+ const store = mockStore()
+ store.dispatch(actions.signTokenTx())
+ assert(tokenSpy.calledOnce)
+ })
+ })
+
+ describe('#lockMetamask', () => {
+ let backgroundSetLockedSpy
+
+ afterEach(() => {
+ backgroundSetLockedSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+
+ backgroundSetLockedSpy = sinon.spy(background, 'setLocked')
+
+ return store.dispatch(actions.lockMetamask())
+ .then(() => {
+ assert(backgroundSetLockedSpy.calledOnce)
+ })
+ })
+
+ it('returns display warning error with value when setLocked in background callback errors', () => {
+ 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'))
+ })
+
+ return store.dispatch(actions.lockMetamask())
+ .then(() => {
+ 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((address, 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('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ setSelectedAddressSpy.callsFake((address, 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', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.addToken())
+ .then(() => {
+ assert(addTokenSpy.calledOnce)
+ })
+ })
+
+ it('errors when addToken in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'UPDATE_TOKENS', newTokens: undefined },
+ ]
+
+ addTokenSpy.callsFake((address, symbol, decimals, image, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.addToken())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#removeToken', () => {
+
+ let removeTokenSpy
+
+ beforeEach(() => {
+ removeTokenSpy = sinon.stub(background, 'removeToken')
+ })
+
+ afterEach(() => {
+ removeTokenSpy.restore()
+ })
+
+ it('calls removeToken in background', () => {
+ const store = mockStore()
+ store.dispatch(actions.removeToken())
+ .then(() => {
+ assert(removeTokenSpy.calledOnce)
+ })
+ })
+
+ it('errors when removeToken in background fails', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ { type: 'UPDATE_TOKENS', newTokens: undefined },
+ ]
+
+ removeTokenSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.removeToken())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#markNoticeRead', () => {
+ let markNoticeReadSpy
+ const notice = {
+ id: 0,
+ read: false,
+ date: 'test date',
+ title: 'test title',
+ body: 'test body',
+ }
+
+ beforeEach(() => {
+ markNoticeReadSpy = sinon.stub(background, 'markNoticeRead')
+ })
+
+ afterEach(() => {
+ markNoticeReadSpy.restore()
+ })
+
+ it('calls markNoticeRead in background', () => {
+ const store = mockStore()
+
+ store.dispatch(actions.markNoticeRead(notice))
+ .then(() => {
+ assert(markNoticeReadSpy.calledOnce)
+ })
+
+ })
+
+ it('errors when markNoticeRead in background throws', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+ markNoticeReadSpy.callsFake((notice, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.markNoticeRead())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setProviderType', () => {
+ let setProviderTypeSpy
+
+ beforeEach(() => {
+ setProviderTypeSpy = sinon.stub(background, 'setProviderType')
+ })
+
+ afterEach(() => {
+ setProviderTypeSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setProviderType())
+ assert(setProviderTypeSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
+ ]
+
+ setProviderTypeSpy.callsFake((type, 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('', () => {
+ const store = mockStore()
+ store.dispatch(actions.setRpcTarget('http://localhost:8545'))
+ assert(setRpcTargetSpy.calledOnce)
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
+ ]
+
+ setRpcTargetSpy.callsFake((newRpc, 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('', () => {
+ const store = mockStore()
+ 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', () => {
+ 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')
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .then((result) => {
+ assert(submitPasswordSpy.calledOnce)
+ assert(exportAccountSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('returns action errors when first func callback errors', () => {
+ 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((password, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('returns action errors when second func callback errors', () => {
+ 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((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#setAccountLabel', () => {
+ let setAccountLabelSpy
+
+ beforeEach(() => {
+ setAccountLabelSpy = sinon.stub(background, 'setAccountLabel')
+ })
+
+ it('', () => {
+ 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)
+ })
+
+ afterEach(() => {
+ nock.restore()
+ })
+
+ it('', () => {
+ 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', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ setFeatureFlagSpy.callsFake((feature, activated, callback) => {
+ callback(new Error('error'))
+ })
+
+ store.dispatch(actions.setFeatureFlag())
+ .catch(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#updateNetworkNonce', () => {
+ let getTransactionCountSpy
+
+ afterEach(() => {
+ getTransactionCountSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ getTransactionCountSpy = sinon.spy(global.ethQuery, 'getTransactionCount')
+
+ store.dispatch(actions.updateNetworkNonce())
+ .then(() => {
+ assert(getTransactionCountSpy.calledOnce)
+ })
+ })
+
+ it('', () => {
+ const store = mockStore()
+ const expectedActions = [
+ { type: 'DISPLAY_WARNING', value: 'error' },
+ ]
+
+ getTransactionCountSpy = sinon.stub(global.ethQuery, 'getTransactionCount')
+ getTransactionCountSpy.callsFake((address, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.updateNetworkNonce())
+ .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((val, 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('', () => {
+ const store = mockStore()
+ setCurrentLocaleSpy = sinon.spy(background, 'setCurrentLocale')
+
+ const expectedActions = [
+ { type: 'SHOW_LOADING_INDICATION', value: undefined },
+ { type: 'HIDE_LOADING_INDICATION' },
+ { type: 'SET_CURRENT_LOCALE', value: 'en' },
+ { type: 'SET_LOCALE_MESSAGES', value: enLocale },
+ ]
+
+ return store.dispatch(actions.updateCurrentLocale('en'))
+ .then(() => {
+ assert(setCurrentLocaleSpy.calledOnce)
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+
+ it('', () => {
+ 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((key, callback) => {
+ callback(new Error('error'))
+ })
+
+ return store.dispatch(actions.updateCurrentLocale('en'))
+ .then(() => {
+ assert.deepEqual(store.getActions(), expectedActions)
+ })
+ })
+ })
+
+ describe('#markPasswordForgotten', () => {
+ let markPasswordForgottenSpy
+
+ beforeEach(() => {
+ markPasswordForgottenSpy = sinon.stub(background, 'markPasswordForgotten')
+ })
+
+ afterEach(() => {
+ markPasswordForgottenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.markPasswordForgotten())
+ assert(markPasswordForgottenSpy.calledOnce)
+ })
+ })
+
+ describe('#unMarkPasswordForgotten', () => {
+ let unMarkPasswordForgottenSpy
+
+ beforeEach(() => {
+ unMarkPasswordForgottenSpy = sinon.stub(background, 'unMarkPasswordForgotten')
+ })
+
+ afterEach(() => {
+ unMarkPasswordForgottenSpy.restore()
+ })
+
+ it('', () => {
+ const store = mockStore()
+ store.dispatch(actions.unMarkPasswordForgotten())
+ assert(unMarkPasswordForgottenSpy.calledOnce)
+ })
+ })
+
+
+})
diff --git a/test/unit/ui/app/components/identicon.spec.js b/test/unit/ui/app/components/identicon.spec.js
new file mode 100644
index 000000000..a2f8d8246
--- /dev/null
+++ b/test/unit/ui/app/components/identicon.spec.js
@@ -0,0 +1,36 @@
+import React from 'react'
+import assert from 'assert'
+import thunk from 'redux-thunk'
+import configureMockStore from 'redux-mock-store'
+import { mount } from 'enzyme'
+
+import IdenticonComponent from '../../../../../ui/app/components/identicon'
+
+describe('Identicon Component', () => {
+
+ const state = {
+ metamask: {
+ useBlockie: false,
+ },
+ }
+
+ const middlewares = [thunk]
+ const mockStore = configureMockStore(middlewares)
+ const store = mockStore(state)
+
+ it('renders default eth_logo identicon with no props', () => {
+ const wrapper = mount(<IdenticonComponent store={store}/>)
+ assert.equal(wrapper.find('img.balance-icon').prop('src'), './images/eth_logo.svg')
+ })
+
+ it('renders custom image and add className props', () => {
+ const wrapper = mount(<IdenticonComponent store={store} className={'test-image'} image={'test-image'} />)
+ assert.equal(wrapper.find('img.test-image').prop('className'), 'test-image identicon')
+ assert.equal(wrapper.find('img.test-image').prop('src'), 'test-image')
+ })
+
+ it('renders div with address prop', () => {
+ const wrapper = mount(<IdenticonComponent store={store} className={'test-address'} address={'0xTest'} />)
+ assert.equal(wrapper.find('div.test-address').prop('className'), 'test-address identicon')
+ })
+})
diff --git a/test/unit/ui/app/components/token-cell.spec.js b/test/unit/ui/app/components/token-cell.spec.js
new file mode 100644
index 000000000..6145c6924
--- /dev/null
+++ b/test/unit/ui/app/components/token-cell.spec.js
@@ -0,0 +1,69 @@
+import React from 'react'
+import assert from 'assert'
+import thunk from 'redux-thunk'
+import { Provider } from 'react-redux'
+import configureMockStore from 'redux-mock-store'
+import { mount } from 'enzyme'
+
+import TokenCell from '../../../../../ui/app/components/token-cell'
+import Identicon from '../../../../../ui/app/components/identicon'
+
+describe('Token Cell', () => {
+ let wrapper
+
+ const state = {
+ metamask: {
+ network: 'test',
+ currentCurrency: 'usd',
+ selectedTokenAddress: '0xToken',
+ selectedAddress: '0xAddress',
+ contractExchangeRates: {
+ '0xAnotherToken': 0.015,
+ },
+ conversionRate: 7.00,
+ },
+ appState: {
+ sidebar: {
+ isOpen: true,
+ },
+ },
+ }
+
+ const middlewares = [thunk]
+ const mockStore = configureMockStore(middlewares)
+ const store = mockStore(state)
+
+ beforeEach(() => {
+ wrapper = mount(
+ <Provider store={store}>
+ <TokenCell
+ address={'0xAnotherToken'}
+ symbol={'TEST'}
+ string={'5.000'}
+ network={22}
+ currentCurrency={'usd'}
+ image={'./test-image'}
+ />
+ </Provider>
+ )
+ })
+
+ it('renders Identicon with props from token cell', () => {
+ assert.equal(wrapper.find(Identicon).prop('address'), '0xAnotherToken')
+ assert.equal(wrapper.find(Identicon).prop('network'), 'test')
+ assert.equal(wrapper.find(Identicon).prop('image'), './test-image')
+ })
+
+ it('renders token balance', () => {
+ assert.equal(wrapper.find('.token-list-item__token-balance').text(), '5.000')
+ })
+
+ it('renders token symbol', () => {
+ assert.equal(wrapper.find('.token-list-item__token-symbol').text(), 'TEST')
+ })
+
+ it('renders converted fiat amount', () => {
+ assert.equal(wrapper.find('.token-list-item__fiat-amount').text(), '0.52 USD')
+ })
+
+})
diff --git a/test/unit/ui/app/selectors.spec.js b/test/unit/ui/app/selectors.spec.js
new file mode 100644
index 000000000..78c4267ee
--- /dev/null
+++ b/test/unit/ui/app/selectors.spec.js
@@ -0,0 +1,175 @@
+const assert = require('assert')
+const selectors = require('../../../../ui/app/selectors')
+const mockState = require('../../../data/mock-state.json')
+const Eth = require('ethjs')
+
+const { createTestProviderTools } = require('../../../stub/provider')
+const provider = createTestProviderTools({ scaffold: {}}).provider
+
+describe('Selectors', function () {
+
+ describe('#getSelectedAddress', function () {
+ let state
+ beforeEach(function () {
+ state = {
+ metamask: {
+ accounts: {
+ '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
+ 'balance': '0x0',
+ 'address': '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+ },
+ },
+ },
+ }
+ })
+
+ it('returns first account if selectedAddress is undefined', function () {
+ assert.equal(selectors.getSelectedAddress(state), '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
+ })
+
+ it('returns selectedAddress', function () {
+ assert.equal(selectors.getSelectedAddress(mockState), '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
+ })
+
+ })
+
+ it('returns selected identity', function () {
+ const identity = selectors.getSelectedIdentity(mockState)
+ assert.equal(identity.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
+ assert.equal(identity.name, 'Test Account')
+ })
+
+ it('returns selected account', function () {
+ const account = selectors.getSelectedAccount(mockState)
+ assert.equal(account.balance, '0x0')
+ assert.equal(account.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
+ })
+
+ it('returns selected token from first token list', function () {
+ const token = selectors.getSelectedToken(mockState)
+ assert.equal(token.address, '0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d')
+ assert.equal(token.symbol, 'TEST')
+ assert.equal(token.decimals, '0')
+ })
+
+ describe('#getSelectedTokenExchangeRate', function () {
+ it('returns token exchange rate for first token', function () {
+ const tokenRate = selectors.getSelectedTokenExchangeRate(mockState)
+ assert.equal(tokenRate, '0.00039345803819379796')
+ })
+ })
+
+
+ describe('#getTokenExchangeRate', function () {
+ let missingTokenRate
+
+ beforeEach(function () {
+ missingTokenRate = {
+ metamask: {
+ 'contractExchangeRates': {},
+ },
+ }
+ })
+
+ it('returns 0 token exchange rate for a token not in state', function () {
+ const tokenRate = selectors.getTokenExchangeRate(missingTokenRate, '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5')
+ assert.equal(tokenRate, 0)
+ })
+
+ it('returns token exchange rate for specified token in state', function () {
+ const tokenRate = selectors.getTokenExchangeRate(mockState, '0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5')
+ assert.equal(tokenRate, 0.00008189274407698049)
+ })
+
+ })
+
+ it('returns conversionRate from state', function () {
+ assert.equal(selectors.conversionRateSelector(mockState), 556.12)
+ })
+
+ it('returns address book from state', function () {
+ const addressBook = selectors.getAddressBook(mockState)
+ assert.equal(addressBook[0].address, '0xc42edfcc21ed14dda456aa0756c153f7985d8813')
+ assert.equal(addressBook[0].name, '')
+ })
+
+ it('returns accounts with balance, address, and name from identity and accounts in state', function () {
+ const accountsWithSendEther = selectors.accountsWithSendEtherInfoSelector(mockState)
+ assert.equal(accountsWithSendEther.length, 2)
+ assert.equal(accountsWithSendEther[0].balance, '0x0')
+ assert.equal(accountsWithSendEther[0].address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
+ assert.equal(accountsWithSendEther[0].name, 'Test Account')
+ })
+
+ it('returns selected account with balance, address, and name from accountsWithSendEtherInfoSelector', function () {
+ const currentAccountwithSendEther = selectors.getCurrentAccountWithSendEtherInfo(mockState)
+ assert.equal(currentAccountwithSendEther.balance, '0x0')
+ assert.equal(currentAccountwithSendEther.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
+ assert.equal(currentAccountwithSendEther.name, 'Test Account')
+ })
+
+ describe('#transactionSelector', function () {
+ it('returns transactions from state', function () {
+ selectors.transactionsSelector(mockState)
+ })
+ })
+
+ it('#getGasIsLoading', () => {
+ const gasIsLoading = selectors.getGasIsLoading(mockState)
+ assert.equal(gasIsLoading, false)
+ })
+
+ describe('Send From', () => {
+ it('#getSendFrom', () => {
+ const sendFrom = selectors.getSendFrom(mockState)
+ assert.equal(sendFrom, '0xc42edfcc21ed14dda456aa0756c153f7985d8813')
+ })
+
+ it('#getForceGasMin', () => {
+ const forceGasMin = selectors.getForceGasMin(mockState)
+ assert.equal(forceGasMin, null)
+ })
+
+ it('#getSendAmount', () => {
+ const sendAmount = selectors.getSendAmount(mockState)
+ assert.equal(sendAmount, '1bc16d674ec80000')
+ })
+
+ it('#getSendMaxModeState', () => {
+ const sendMaxModeState = selectors.getSendMaxModeState(mockState)
+ assert.equal(sendMaxModeState, false)
+ })
+ })
+
+ it('#getCurrentCurrency', () => {
+ const currentCurrency = selectors.getCurrentCurrency(mockState)
+ assert.equal(currentCurrency, 'usd')
+ })
+
+ it('#getSelectedTokenToFiatRate', () => {
+ const selectedTokenToFiatRate = selectors.getSelectedTokenToFiatRate(mockState)
+ assert.equal(selectedTokenToFiatRate, '0.21880988420033493')
+ })
+
+ describe('#getSelectedTokenContract', () => {
+
+ beforeEach(() => {
+ global.eth = new Eth(provider)
+ })
+
+ it('', () => {
+ const selectedTokenContract = selectors.getSelectedTokenContract(mockState)
+ assert(selectedTokenContract.abi)
+ })
+ })
+
+ it('#getCurrentViewContext', () => {
+ const currentViewContext = selectors.getCurrentViewContext(mockState)
+ assert.equal(currentViewContext, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
+ })
+
+ it('#getTotalUnapprovedCount', () => {
+ const totalUnapprovedCount = selectors.getTotalUnapprovedCount(mockState)
+ assert.equal(totalUnapprovedCount, 1)
+ })
+})
diff --git a/test/unit/ui/etherscan-prefix-for-network.spec.js b/test/unit/ui/etherscan-prefix-for-network.spec.js
new file mode 100644
index 000000000..f0aeb8306
--- /dev/null
+++ b/test/unit/ui/etherscan-prefix-for-network.spec.js
@@ -0,0 +1,26 @@
+const assert = require('assert')
+const etherscanNetworkPrefix = require('../../../ui/lib/etherscan-prefix-for-network')
+
+describe('Etherscan Network Prefix', () => {
+
+ it('returns empy string as default value', () => {
+ assert.equal(etherscanNetworkPrefix(), '')
+ })
+
+ it('returns empty string as a prefix for networkId of 1', () => {
+ assert.equal(etherscanNetworkPrefix(1), '')
+ })
+
+ it('returns ropsten as prefix for networkId of 3', () => {
+ assert.equal(etherscanNetworkPrefix(3), 'ropsten.')
+ })
+
+ it('returns rinkeby as prefix for networkId of 4', () => {
+ assert.equal(etherscanNetworkPrefix(4), 'rinkeby.')
+ })
+
+ it('returs kovan as prefix for networkId of 42', () => {
+ assert.equal(etherscanNetworkPrefix(42), 'kovan.')
+ })
+
+})