aboutsummaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2017-02-16 08:09:16 +0800
committerDan Finlay <dan@danfinlay.com>2017-02-16 08:09:16 +0800
commit6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf (patch)
tree47906b974dee789ffd544b4c85da67e6f2323016 /test/unit
parent245e779f37763ce0633119c257877706d0bf3554 (diff)
parent943bcec0d702b2c70b323000ed25d3c425e2a44f (diff)
downloadtangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.gz
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.zst
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.zip
Merge branch 'kumavis-patch-1' of github.com:MetaMask/metamask-plugin into kumavis-patch-1
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/account-link-test.js10
-rw-r--r--test/unit/actions/restore_vault_test.js60
-rw-r--r--test/unit/actions/set_selected_account_test.js3
-rw-r--r--test/unit/actions/tx_test.js14
-rw-r--r--test/unit/config-manager-test.js212
-rw-r--r--test/unit/currency-controller-test.js87
-rw-r--r--test/unit/explorer-link-test.js2
-rw-r--r--test/unit/extension-test.js41
-rw-r--r--test/unit/idStore-migration-test.js83
-rw-r--r--test/unit/idStore-test.js85
-rw-r--r--test/unit/keyring-controller-test.js172
-rw-r--r--test/unit/keyrings/hd-test.js127
-rw-r--r--test/unit/keyrings/simple-test.js149
-rw-r--r--test/unit/message-manager-test.js89
-rw-r--r--test/unit/metamask-controller-test.js29
-rw-r--r--test/unit/migrations-test.js105
-rw-r--r--test/unit/nodeify-test.js22
-rw-r--r--test/unit/notice-controller-test.js116
-rw-r--r--test/unit/tx-manager-test.js211
-rw-r--r--test/unit/util_test.js22
20 files changed, 1320 insertions, 319 deletions
diff --git a/test/unit/account-link-test.js b/test/unit/account-link-test.js
index 39889b6be..4ea12e002 100644
--- a/test/unit/account-link-test.js
+++ b/test/unit/account-link-test.js
@@ -3,9 +3,15 @@ var linkGen = require('../../ui/lib/account-link')
describe('account-link', function() {
- it('adds testnet prefix to morden test network', function() {
+ it('adds morden prefix to morden test network', function() {
var result = linkGen('account', '2')
- assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
+ assert.notEqual(result.indexOf('morden'), -1, 'testnet included')
+ assert.notEqual(result.indexOf('account'), -1, 'account included')
+ })
+
+ it('adds testnet prefix to ropsten test network', function() {
+ var result = linkGen('account', '3')
+ assert.notEqual(result.indexOf('testnet'), -1, 'testnet included')
assert.notEqual(result.indexOf('account'), -1, 'account included')
})
diff --git a/test/unit/actions/restore_vault_test.js b/test/unit/actions/restore_vault_test.js
deleted file mode 100644
index 609f5429e..000000000
--- a/test/unit/actions/restore_vault_test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-var jsdom = require('mocha-jsdom')
-var assert = require('assert')
-var freeze = require('deep-freeze-strict')
-var path = require('path')
-var sinon = require('sinon')
-
-var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
-var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-
-describe('#recoverFromSeed(password, seed)', function() {
-
- beforeEach(function() {
- // sinon allows stubbing methods that are easily verified
- this.sinon = sinon.sandbox.create()
- })
-
- afterEach(function() {
- // sinon requires cleanup otherwise it will overwrite context
- this.sinon.restore()
- })
-
- // stub out account manager
- actions._setAccountManager({
- recoverFromSeed(pw, seed, cb) {
- cb(null, {
- identities: {
- foo: 'bar'
- }
- })
- },
- })
-
- it('sets metamask.isUnlocked to true', function() {
- var initialState = {
- metamask: {
- isUnlocked: false,
- isInitialized: false,
- }
- }
- freeze(initialState)
-
- const restorePhrase = 'invite heavy among daring outdoor dice jelly coil stable note seat vicious'
- const password = 'foo'
- const dispatchFunc = actions.recoverFromSeed(password, restorePhrase)
-
- var dispatchStub = this.sinon.stub()
- dispatchStub.withArgs({ TYPE: actions.unlockMetamask() }).onCall(0)
- dispatchStub.withArgs({ TYPE: actions.showAccountsPage() }).onCall(1)
-
- var action
- var resultingState = initialState
- dispatchFunc((newAction) => {
- action = newAction
- resultingState = reducers(resultingState, action)
- })
-
- assert.equal(resultingState.metamask.isUnlocked, true, 'was unlocked')
- assert.equal(resultingState.metamask.isInitialized, true, 'was initialized')
- });
-});
diff --git a/test/unit/actions/set_selected_account_test.js b/test/unit/actions/set_selected_account_test.js
index 69eb11e47..2dc42d2ec 100644
--- a/test/unit/actions/set_selected_account_test.js
+++ b/test/unit/actions/set_selected_account_test.js
@@ -31,7 +31,7 @@ describe('SHOW_ACCOUNT_DETAIL', function() {
it('updates metamask state', function() {
var initialState = {
metamask: {
- selectedAccount: 'foo'
+ selectedAddress: 'foo'
}
}
freeze(initialState)
@@ -43,7 +43,6 @@ describe('SHOW_ACCOUNT_DETAIL', function() {
freeze(action)
var resultingState = reducers(initialState, action)
- assert.equal(resultingState.metamask.selectedAccount, action.value)
assert.equal(resultingState.metamask.selectedAddress, action.value)
})
})
diff --git a/test/unit/actions/tx_test.js b/test/unit/actions/tx_test.js
index c08a8aa26..7ded5b1ef 100644
--- a/test/unit/actions/tx_test.js
+++ b/test/unit/actions/tx_test.js
@@ -31,7 +31,7 @@ describe('tx confirmation screen', function() {
},
},
metamask: {
- unconfTxs: {
+ unapprovedTxs: {
'1457634084250832': {
id: 1457634084250832,
status: "unconfirmed",
@@ -46,7 +46,7 @@ describe('tx confirmation screen', function() {
describe('cancelTx', function() {
before(function(done) {
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb('An error!') },
cancelTransaction(txId) { /* noop */ },
clearSeedWordCache(cb) { cb() },
@@ -75,7 +75,7 @@ describe('tx confirmation screen', function() {
before(function(done) {
alert = () => {/* noop */}
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb({message: 'An error!'}) },
})
@@ -96,7 +96,7 @@ describe('tx confirmation screen', function() {
describe('when there is success', function() {
it('should complete tx and go home', function() {
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb() },
})
@@ -119,7 +119,7 @@ describe('tx confirmation screen', function() {
},
},
metamask: {
- unconfTxs: {
+ unapprovedTxs: {
'1457634084250832': {
id: 1457634084250832,
status: "unconfirmed",
@@ -135,7 +135,7 @@ describe('tx confirmation screen', function() {
}
freeze(initialState)
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb() },
})
@@ -162,7 +162,7 @@ describe('tx confirmation screen', function() {
});
function getUnconfirmedTxCount(state) {
- var txs = state.metamask.unconfTxs
+ var txs = state.metamask.unapprovedTxs
var count = Object.keys(txs).length
return count
}
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
index 6aa7146f0..05324e741 100644
--- a/test/unit/config-manager-test.js
+++ b/test/unit/config-manager-test.js
@@ -1,136 +1,20 @@
+// polyfill fetch
+global.fetch = global.fetch || require('isomorphic-fetch')
+
const assert = require('assert')
const extend = require('xtend')
-const STORAGE_KEY = 'metamask-persistance-key'
-var configManagerGen = require('../lib/mock-config-manager')
-var configManager
const rp = require('request-promise')
const nock = require('nock')
+const configManagerGen = require('../lib/mock-config-manager')
describe('config-manager', function() {
+ var configManager
beforeEach(function() {
- window.localStorage = {} // Hacking localStorage support into JSDom
configManager = configManagerGen()
})
- describe('currency conversions', function() {
-
- describe('#getCurrentFiat', function() {
- it('should return false if no previous key exists', function() {
- var result = configManager.getCurrentFiat()
- assert.ok(!result)
- })
- })
-
- describe('#setCurrentFiat', function() {
- it('should make getCurrentFiat return true once set', function() {
- assert.equal(configManager.getCurrentFiat(), false)
- configManager.setCurrentFiat('USD')
- var result = configManager.getCurrentFiat()
- assert.equal(result, 'USD')
- })
-
- it('should work with other currencies as well', function() {
- assert.equal(configManager.getCurrentFiat(), false)
- configManager.setCurrentFiat('JPY')
- var result = configManager.getCurrentFiat()
- assert.equal(result, 'JPY')
- })
- })
-
- describe('#getConversionRate', function() {
- it('should return false if non-existent', function() {
- var result = configManager.getConversionRate()
- assert.ok(!result)
- })
- })
-
- describe('#updateConversionRate', function() {
- it('should retrieve an update for ETH to USD and set it in memory', function(done) {
- this.timeout(15000)
- var usdMock = nock('https://www.cryptonator.com')
- .get('/api/ticker/eth-USD')
- .reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
-
- assert.equal(configManager.getConversionRate(), false)
- var promise = new Promise(
- function (resolve, reject) {
- configManager.setCurrentFiat('USD')
- configManager.updateConversionRate().then(function() {
- resolve()
- })
- })
-
- promise.then(function() {
- var result = configManager.getConversionRate()
- assert.equal(typeof result, 'number')
- done()
- }).catch(function(err) {
- console.log(err)
- })
-
- })
-
- it('should work for JPY as well.', function() {
- this.timeout(15000)
- assert.equal(configManager.getConversionRate(), false)
-
- var jpyMock = nock('https://www.cryptonator.com')
- .get('/api/ticker/eth-JPY')
- .reply(200, '{"ticker":{"base":"ETH","target":"JPY","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
-
-
- var promise = new Promise(
- function (resolve, reject) {
- configManager.setCurrentFiat('JPY')
- configManager.updateConversionRate().then(function() {
- resolve()
- })
- })
-
- promise.then(function() {
- var result = configManager.getConversionRate()
- assert.equal(typeof result, 'number')
- }).catch(function(err) {
- console.log(err)
- })
- })
- })
- })
-
- describe('confirmation', function() {
-
- describe('#getConfirmed', function() {
- it('should return false if no previous key exists', function() {
- var result = configManager.getConfirmed()
- assert.ok(!result)
- })
- })
-
- describe('#setConfirmed', function() {
- it('should make getConfirmed return true once set', function() {
- assert.equal(configManager.getConfirmed(), false)
- configManager.setConfirmed(true)
- var result = configManager.getConfirmed()
- assert.equal(result, true)
- })
-
- it('should be able to set false', function() {
- configManager.setConfirmed(false)
- var result = configManager.getConfirmed()
- assert.equal(result, false)
- })
-
- it('should persist to local storage', function() {
- configManager.setConfirmed(true)
- var data = configManager.getData()
- assert.equal(data.isConfirmed, true)
- })
- })
- })
-
describe('#setConfig', function() {
- window.localStorage = {} // Hacking localStorage support into JSDom
it('should set the config key', function () {
var testConfig = {
@@ -153,7 +37,6 @@ describe('config-manager', function() {
rpcTarget: 'foobar'
},
}
- configManager.setConfirmed(true)
configManager.setConfig(testConfig)
var testWallet = {
@@ -164,7 +47,6 @@ describe('config-manager', function() {
var 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(configManager.getConfirmed(), true)
testConfig.provider.type = 'something else!'
configManager.setConfig(testConfig)
@@ -173,7 +55,6 @@ describe('config-manager', function() {
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)
- assert.equal(configManager.getConfirmed(), true)
})
})
@@ -215,7 +96,7 @@ describe('config-manager', function() {
describe('transactions', function() {
beforeEach(function() {
- configManager._saveTxList([])
+ configManager.setTxList([])
})
describe('#getTxList', function() {
@@ -226,90 +107,13 @@ describe('config-manager', function() {
})
})
- describe('#_saveTxList', function() {
+ describe('#setTxList', function() {
it('saves the submitted data to the tx list', function() {
var target = [{ foo: 'bar' }]
- configManager._saveTxList(target)
+ configManager.setTxList(target)
var result = configManager.getTxList()
assert.equal(result[0].foo, 'bar')
})
})
-
- describe('#addTx', function() {
- it('adds a tx returned in getTxList', function() {
- var tx = { id: 1 }
- configManager.addTx(tx)
- var result = configManager.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].id, 1)
- })
-
- it('cuts off early txs beyond a limit', function() {
- const limit = configManager.txLimit
- for (let i = 0; i < limit + 1; i++) {
- let tx = { id: i }
- configManager.addTx(tx)
- }
- var result = configManager.getTxList()
- assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
- assert.equal(result[0].id, 1, 'early txs truncted')
- })
- })
-
- describe('#confirmTx', function() {
- it('sets the tx status to confirmed', function() {
- var tx = { id: 1, status: 'unconfirmed' }
- configManager.addTx(tx)
- configManager.confirmTx(1)
- var result = configManager.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].status, 'confirmed')
- })
- })
-
- describe('#rejectTx', function() {
- it('sets the tx status to rejected', function() {
- var tx = { id: 1, status: 'unconfirmed' }
- configManager.addTx(tx)
- configManager.rejectTx(1)
- var result = configManager.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].status, 'rejected')
- })
- })
-
- describe('#updateTx', function() {
- it('replaces the tx with the same id', function() {
- configManager.addTx({ id: '1', status: 'unconfirmed' })
- configManager.addTx({ id: '2', status: 'confirmed' })
- configManager.updateTx({ id: '1', status: 'blah', hash: 'foo' })
- var result = configManager.getTx('1')
- assert.equal(result.hash, 'foo')
- })
- })
-
- describe('#unconfirmedTxs', function() {
- it('returns unconfirmed txs in a hash', function() {
- configManager.addTx({ id: '1', status: 'unconfirmed' })
- configManager.addTx({ id: '2', status: 'confirmed' })
- let result = configManager.unconfirmedTxs()
- assert.equal(typeof result, 'object')
- assert.equal(result['1'].status, 'unconfirmed')
- assert.equal(result['0'], undefined)
- assert.equal(result['2'], undefined)
- })
- })
-
- describe('#getTx', function() {
- it('returns a tx with the requested id', function() {
- configManager.addTx({ id: '1', status: 'unconfirmed' })
- configManager.addTx({ id: '2', status: 'confirmed' })
- assert.equal(configManager.getTx('1').status, 'unconfirmed')
- assert.equal(configManager.getTx('2').status, 'confirmed')
- })
- })
})
})
diff --git a/test/unit/currency-controller-test.js b/test/unit/currency-controller-test.js
new file mode 100644
index 000000000..c57b522c7
--- /dev/null
+++ b/test/unit/currency-controller-test.js
@@ -0,0 +1,87 @@
+// polyfill fetch
+global.fetch = global.fetch || require('isomorphic-fetch')
+
+const assert = require('assert')
+const extend = require('xtend')
+const rp = require('request-promise')
+const nock = require('nock')
+const CurrencyController = require('../../app/scripts/lib/controllers/currency')
+
+describe('config-manager', function() {
+ var currencyController
+
+ beforeEach(function() {
+ currencyController = new CurrencyController()
+ })
+
+ describe('currency conversions', function() {
+
+ describe('#setCurrentCurrency', function() {
+ it('should return USD as default', function() {
+ assert.equal(currencyController.getCurrentCurrency(), 'USD')
+ })
+
+ it('should be able to set to other currency', function() {
+ assert.equal(currencyController.getCurrentCurrency(), 'USD')
+ currencyController.setCurrentCurrency('JPY')
+ var result = currencyController.getCurrentCurrency()
+ assert.equal(result, 'JPY')
+ })
+ })
+
+ describe('#getConversionRate', function() {
+ it('should return undefined if non-existent', function() {
+ var result = currencyController.getConversionRate()
+ assert.ok(!result)
+ })
+ })
+
+ describe('#updateConversionRate', function() {
+ it('should retrieve an update for ETH to USD and set it in memory', function(done) {
+ this.timeout(15000)
+ var usdMock = nock('https://www.cryptonator.com')
+ .get('/api/ticker/eth-USD')
+ .reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
+
+ assert.equal(currencyController.getConversionRate(), 0)
+ currencyController.setCurrentCurrency('USD')
+ currencyController.updateConversionRate()
+ .then(function() {
+ var result = currencyController.getConversionRate()
+ console.log('currencyController.getConversionRate:', result)
+ assert.equal(typeof result, 'number')
+ done()
+ }).catch(function(err) {
+ done(err)
+ })
+
+ })
+
+ it('should work for JPY as well.', function() {
+ this.timeout(15000)
+ assert.equal(currencyController.getConversionRate(), 0)
+
+ var jpyMock = nock('https://www.cryptonator.com')
+ .get('/api/ticker/eth-JPY')
+ .reply(200, '{"ticker":{"base":"ETH","target":"JPY","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
+
+
+ var promise = new Promise(
+ function (resolve, reject) {
+ currencyController.setCurrentCurrency('JPY')
+ currencyController.updateConversionRate().then(function() {
+ resolve()
+ })
+ })
+
+ promise.then(function() {
+ var result = currencyController.getConversionRate()
+ assert.equal(typeof result, 'number')
+ }).catch(function(err) {
+ done(err)
+ })
+ })
+ })
+ })
+
+})
diff --git a/test/unit/explorer-link-test.js b/test/unit/explorer-link-test.js
index 961b400fd..8aa58bff9 100644
--- a/test/unit/explorer-link-test.js
+++ b/test/unit/explorer-link-test.js
@@ -4,7 +4,7 @@ var linkGen = require('../../ui/lib/explorer-link')
describe('explorer-link', function() {
it('adds testnet prefix to morden test network', function() {
- var result = linkGen('hash', '2')
+ var result = linkGen('hash', '3')
assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
})
diff --git a/test/unit/extension-test.js b/test/unit/extension-test.js
index 6b695e835..8f259f05c 100644
--- a/test/unit/extension-test.js
+++ b/test/unit/extension-test.js
@@ -1,19 +1,47 @@
var assert = require('assert')
var sinon = require('sinon')
const ethUtil = require('ethereumjs-util')
-GLOBAL.chrome = {}
-GLOBAL.browser = {}
+global.chrome = {}
+global.browser = {}
var path = require('path')
var Extension = require(path.join(__dirname, '..', '..', 'app', 'scripts', 'lib', 'extension-instance.js'))
describe('extension', function() {
+ describe('extension.getURL', function() {
+ const desiredResult = 'http://the-desired-result.io'
+
+ describe('in Chrome or Firefox', function() {
+ global.chrome.extension = {
+ getURL: () => desiredResult
+ }
+
+ it('returns the desired result', function() {
+ const extension = new Extension()
+ const result = extension.extension.getURL()
+ assert.equal(result, desiredResult)
+ })
+ })
+
+ describe('in Microsoft Edge', function() {
+ global.browser.extension = {
+ getURL: () => desiredResult
+ }
+
+ it('returns the desired result', function() {
+ const extension = new Extension()
+ const result = extension.extension.getURL()
+ assert.equal(result, desiredResult)
+ })
+ })
+ })
+
describe('with chrome global', function() {
let extension
beforeEach(function() {
- GLOBAL.chrome = {
+ global.chrome = {
alarms: 'foo'
}
extension = new Extension()
@@ -30,9 +58,9 @@ describe('extension', function() {
beforeEach(function() {
realWindow = window
- window = GLOBAL
- GLOBAL.chrome = undefined
- GLOBAL.alarms = 'foo'
+ window = global
+ global.chrome = undefined
+ global.alarms = 'foo'
extension = new Extension()
})
@@ -45,4 +73,5 @@ describe('extension', function() {
})
})
+
})
diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js
new file mode 100644
index 000000000..81a99ef63
--- /dev/null
+++ b/test/unit/idStore-migration-test.js
@@ -0,0 +1,83 @@
+const async = require('async')
+const assert = require('assert')
+const ObservableStore = require('obs-store')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const ConfigManager = require('../../app/scripts/lib/config-manager')
+const firstTimeState = require('../../app/scripts/first-time-state')
+const delegateCallCode = require('../lib/example-code.json').delegateCallCode
+const clone = require('clone')
+
+// The old way:
+const IdentityStore = require('../../app/scripts/lib/idStore')
+const STORAGE_KEY = 'metamask-config'
+
+// The new ways:
+var KeyringController = require('../../app/scripts/keyring-controller')
+const mockEncryptor = require('../lib/mock-encryptor')
+const MockSimpleKeychain = require('../lib/mock-simple-keychain')
+const sinon = require('sinon')
+
+const mockVault = {
+ seed: 'picnic injury awful upper eagle junk alert toss flower renew silly vague',
+ account: '0x5d8de92c205279c10e5669f797b853ccef4f739a',
+}
+
+const badVault = {
+ seed: 'radar blur cabbage chef fix engine embark joy scheme fiction master release',
+}
+
+describe('IdentityStore to KeyringController migration', function() {
+
+ // The stars of the show:
+ let idStore, keyringController, seedWords, configManager
+
+ let password = 'password123'
+ let entropy = 'entripppppyy duuude'
+ let accounts = []
+ let newAccounts = []
+ let originalKeystore
+
+ // This is a lot of setup, I know!
+ // We have to create an old style vault, populate it,
+ // and THEN create a new one, before we can run tests on it.
+ beforeEach(function(done) {
+ this.sinon = sinon.sandbox.create()
+ let store = new ObservableStore(clone(firstTimeState))
+ configManager = new ConfigManager({ store })
+
+ idStore = new IdentityStore({
+ configManager: configManager,
+ ethStore: {
+ addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
+ del(acct) { delete accounts[acct] },
+ },
+ })
+
+ idStore._createVault(password, mockVault.seed, (err) => {
+ assert.ifError(err, 'createNewVault threw error')
+ originalKeystore = idStore._idmgmt.keyStore
+
+ idStore.setLocked((err) => {
+ assert.ifError(err, 'createNewVault threw error')
+ keyringController = new KeyringController({
+ configManager,
+ ethStore: {
+ addAccount(acct) { newAccounts.push(ethUtil.addHexPrefix(acct)) },
+ del(acct) { delete newAccounts[acct] },
+ },
+ txManager: {
+ getTxList: () => [],
+ getUnapprovedTxList: () => []
+ },
+ })
+
+ // Stub out the browser crypto for a mock encryptor.
+ // Browser crypto is tested in the integration test suite.
+ keyringController.encryptor = mockEncryptor
+ done()
+ })
+ })
+ })
+
+})
diff --git a/test/unit/idStore-test.js b/test/unit/idStore-test.js
index ee4613236..000c58a82 100644
--- a/test/unit/idStore-test.js
+++ b/test/unit/idStore-test.js
@@ -1,13 +1,16 @@
-var assert = require('assert')
-var IdentityStore = require('../../app/scripts/lib/idStore')
-var configManagerGen = require('../lib/mock-config-manager')
+const async = require('async')
+const assert = require('assert')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const configManagerGen = require('../lib/mock-config-manager')
+const delegateCallCode = require('../lib/example-code.json').delegateCallCode
+const IdentityStore = require('../../app/scripts/lib/idStore')
describe('IdentityStore', function() {
describe('#createNewVault', function () {
let idStore
let password = 'password123'
- let entropy = 'entripppppyy duuude'
let seedWords
let accounts = []
let originalKeystore
@@ -18,11 +21,12 @@ describe('IdentityStore', function() {
idStore = new IdentityStore({
configManager: configManagerGen(),
ethStore: {
- addAccount(acct) { accounts.push(acct) },
+ addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
},
})
- idStore.createNewVault(password, entropy, (err, seeds) => {
+ idStore.createNewVault(password, (err, seeds) => {
+ assert.ifError(err, 'createNewVault threw error')
seedWords = seeds
originalKeystore = idStore._idmgmt.keyStore
done()
@@ -38,7 +42,7 @@ describe('IdentityStore', function() {
idStore = new IdentityStore({
configManager: configManagerGen(),
ethStore: {
- addAccount(acct) { newAccounts.push(acct) },
+ addAccount(acct) { newAccounts.push(ethUtil.addHexPrefix(acct)) },
},
})
})
@@ -57,31 +61,80 @@ describe('IdentityStore', function() {
})
describe('#recoverFromSeed BIP44 compliance', function() {
- let seedWords = 'picnic injury awful upper eagle junk alert toss flower renew silly vague'
- let firstAccount = '0x5d8de92c205279c10e5669f797b853ccef4f739a'
+ const salt = 'lightwalletSalt'
let password = 'secret!'
- let accounts = []
+ let accounts = {}
let idStore
+ var assertions = [
+ {
+ seed: 'picnic injury awful upper eagle junk alert toss flower renew silly vague',
+ account: '0x5d8de92c205279c10e5669f797b853ccef4f739a',
+ },
+ {
+ seed: 'radar blur cabbage chef fix engine embark joy scheme fiction master release',
+ account: '0xe15d894becb0354c501ae69429b05143679f39e0',
+ },
+ {
+ seed: 'phone coyote caught pattern found table wedding list tumble broccoli chief swing',
+ account: '0xb0e868f24bc7fec2bce2efc2b1c344d7569cd9d2',
+ },
+ {
+ seed: 'recycle tag bird palace blue village anxiety census cook soldier example music',
+ account: '0xab34a45920afe4af212b96ec51232aaa6a33f663',
+ },
+ {
+ seed: 'half glimpse tape cute harvest sweet bike voyage actual floor poet lazy',
+ account: '0x28e9044597b625ac4beda7250011670223de43b2',
+ },
+ {
+ seed: 'flavor tiger carpet motor angry hungry document inquiry large critic usage liar',
+ account: '0xb571be96558940c4e9292e1999461aa7499fb6cd',
+ },
+ ]
+
before(function() {
window.localStorage = {} // Hacking localStorage support into JSDom
idStore = new IdentityStore({
configManager: configManagerGen(),
ethStore: {
- addAccount(acct) { accounts.push(acct) },
+ addAccount(acct) { accounts[acct] = acct},
+ del(acct) { delete accounts[acct] },
},
})
})
- it('should return the expected first account', function (done) {
+ it('should enforce seed compliance with TestRPC', function (done) {
+ this.timeout(10000)
+ const tests = assertions.map((assertion) => {
+ return function (cb) {
- idStore.recoverFromSeed(password, seedWords, (err) => {
- assert.ifError(err)
+ idStore.recoverFromSeed(password, assertion.seed, (err) => {
+ assert.ifError(err)
+
+ var expected = assertion.account.toLowerCase()
+ var received = accounts[expected].toLowerCase()
+ assert.equal(received, expected)
+
+ idStore.tryPassword(password, function (err) {
- let newKeystore = idStore._idmgmt.keyStore
- assert.equal(accounts[0], firstAccount)
+ assert.ok(idStore._isUnlocked(), 'should unlock the id store')
+
+ idStore.submitPassword(password, function(err, account) {
+ assert.ifError(err)
+ assert.equal(account, expected)
+ assert.equal(Object.keys(idStore._getAddresses()).length, 1, 'only one account on restore')
+ cb()
+ })
+ })
+ })
+ }
+ })
+
+ async.series(tests, function(err, results) {
+ assert.ifError(err)
done()
})
})
diff --git a/test/unit/keyring-controller-test.js b/test/unit/keyring-controller-test.js
new file mode 100644
index 000000000..aae4cdfd6
--- /dev/null
+++ b/test/unit/keyring-controller-test.js
@@ -0,0 +1,172 @@
+const assert = require('assert')
+const KeyringController = require('../../app/scripts/keyring-controller')
+const configManagerGen = require('../lib/mock-config-manager')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const async = require('async')
+const mockEncryptor = require('../lib/mock-encryptor')
+const MockSimpleKeychain = require('../lib/mock-simple-keychain')
+const sinon = require('sinon')
+
+describe('KeyringController', function() {
+
+ let keyringController, state
+ let password = 'password123'
+ let seedWords = 'puzzle seed penalty soldier say clay field arctic metal hen cage runway'
+ let addresses = ['eF35cA8EbB9669A35c31b5F6f249A9941a812AC1'.toLowerCase()]
+ let accounts = []
+ let originalKeystore
+
+ beforeEach(function(done) {
+ this.sinon = sinon.sandbox.create()
+ window.localStorage = {} // Hacking localStorage support into JSDom
+
+ keyringController = new KeyringController({
+ configManager: configManagerGen(),
+ txManager: {
+ getTxList: () => [],
+ getUnapprovedTxList: () => []
+ },
+ ethStore: {
+ addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
+ },
+ })
+
+ // Stub out the browser crypto for a mock encryptor.
+ // Browser crypto is tested in the integration test suite.
+ keyringController.encryptor = mockEncryptor
+
+ keyringController.createNewVaultAndKeychain(password)
+ .then(function (newState) {
+ state = newState
+ done()
+ })
+ .catch((err) => {
+ done(err)
+ })
+ })
+
+ afterEach(function() {
+ // Cleanup mocks
+ this.sinon.restore()
+ })
+
+ describe('#createNewVaultAndKeychain', function () {
+ this.timeout(10000)
+
+ it('should set a vault on the configManager', function(done) {
+ keyringController.store.updateState({ vault: null })
+ assert(!keyringController.store.getState().vault, 'no previous vault')
+ keyringController.createNewVaultAndKeychain(password)
+ .then(() => {
+ const vault = keyringController.store.getState().vault
+ assert(vault, 'vault created')
+ done()
+ })
+ .catch((reason) => {
+ done(reason)
+ })
+ })
+ })
+
+ describe('#restoreKeyring', function() {
+
+ it(`should pass a keyring's serialized data back to the correct type.`, function(done) {
+ const mockSerialized = {
+ type: 'HD Key Tree',
+ data: {
+ mnemonic: seedWords,
+ numberOfAccounts: 1,
+ }
+ }
+ const mock = this.sinon.mock(keyringController)
+
+ mock.expects('getBalanceAndNickname')
+ .exactly(1)
+
+ keyringController.restoreKeyring(mockSerialized)
+ .then((keyring) => {
+ assert.equal(keyring.wallets.length, 1, 'one wallet restored')
+ return keyring.getAccounts()
+ })
+ .then((accounts) => {
+ assert.equal(accounts[0], addresses[0])
+ mock.verify()
+ done()
+ })
+ .catch((reason) => {
+ done(reason)
+ })
+ })
+ })
+
+ describe('#createNickname', function() {
+ it('should add the address to the identities hash', function() {
+ const fakeAddress = '0x12345678'
+ keyringController.createNickname(fakeAddress)
+ const identities = keyringController.memStore.getState().identities
+ const identity = identities[fakeAddress]
+ assert.equal(identity.address, fakeAddress)
+ })
+ })
+
+ describe('#saveAccountLabel', function() {
+ it ('sets the nickname', function(done) {
+ const account = addresses[0]
+ var nick = 'Test nickname'
+ const identities = keyringController.memStore.getState().identities
+ identities[ethUtil.addHexPrefix(account)] = {}
+ keyringController.memStore.updateState({ identities })
+ keyringController.saveAccountLabel(account, nick)
+ .then((label) => {
+ try {
+ assert.equal(label, nick)
+ const persisted = keyringController.store.getState().walletNicknames[account]
+ assert.equal(persisted, nick)
+ done()
+ } catch (err) {
+ done()
+ }
+ })
+ .catch((reason) => {
+ done(reason)
+ })
+ })
+ })
+
+ describe('#getAccounts', function() {
+ it('returns the result of getAccounts for each keyring', function() {
+ keyringController.keyrings = [
+ { getAccounts() { return Promise.resolve([1,2,3]) } },
+ { getAccounts() { return Promise.resolve([4,5,6]) } },
+ ]
+
+ keyringController.getAccounts()
+ .then((result) => {
+ assert.deepEqual(result, [1,2,3,4,5,6])
+ done()
+ })
+ })
+ })
+
+ describe('#addGasBuffer', function() {
+ it('adds 100k gas buffer to estimates', function() {
+
+ const gas = '0x04ee59' // Actual estimated gas example
+ const tooBigOutput = '0x80674f9' // Actual bad output
+ const bnGas = new BN(ethUtil.stripHexPrefix(gas), 16)
+ const correctBuffer = new BN('100000', 10)
+ const correct = bnGas.add(correctBuffer)
+
+ const tooBig = new BN(tooBigOutput, 16)
+ const result = keyringController.addGasBuffer(gas)
+ const bnResult = new BN(ethUtil.stripHexPrefix(result), 16)
+
+ assert.equal(result.indexOf('0x'), 0, 'included hex prefix')
+ assert(bnResult.gt(bnGas), 'Estimate increased in value.')
+ assert.equal(bnResult.sub(bnGas).toString(10), '100000', 'added 100k gas')
+ assert.equal(result, '0x' + correct.toString(16), 'Added the right amount')
+ assert.notEqual(result, tooBigOutput, 'not that bad estimate')
+ })
+ })
+})
diff --git a/test/unit/keyrings/hd-test.js b/test/unit/keyrings/hd-test.js
new file mode 100644
index 000000000..dfc0ec908
--- /dev/null
+++ b/test/unit/keyrings/hd-test.js
@@ -0,0 +1,127 @@
+const assert = require('assert')
+const extend = require('xtend')
+const HdKeyring = require('../../../app/scripts/keyrings/hd')
+
+// Sample account:
+const privKeyHex = 'b8a9c05beeedb25df85f8d641538cbffedf67216048de9c678ee26260eb91952'
+
+const sampleMnemonic = 'finish oppose decorate face calm tragic certain desk hour urge dinosaur mango'
+const firstAcct = '1c96099350f13d558464ec79b9be4445aa0ef579'
+const secondAcct = '1b00aed43a693f3a957f9feb5cc08afa031e37a0'
+
+describe('hd-keyring', function() {
+
+ let keyring
+ beforeEach(function() {
+ keyring = new HdKeyring()
+ })
+
+ describe('constructor', function(done) {
+ keyring = new HdKeyring({
+ mnemonic: sampleMnemonic,
+ numberOfAccounts: 2,
+ })
+
+ const accounts = keyring.getAccounts()
+ .then((accounts) => {
+ assert.equal(accounts[0], firstAcct)
+ assert.equal(accounts[1], secondAcct)
+ done()
+ })
+ })
+
+ describe('Keyring.type', function() {
+ it('is a class property that returns the type string.', function() {
+ const type = HdKeyring.type
+ assert.equal(typeof type, 'string')
+ })
+ })
+
+ describe('#type', function() {
+ it('returns the correct value', function() {
+ const type = keyring.type
+ const correct = HdKeyring.type
+ assert.equal(type, correct)
+ })
+ })
+
+ describe('#serialize empty wallets.', function() {
+ it('serializes a new mnemonic', function() {
+ keyring.serialize()
+ .then((output) => {
+ assert.equal(output.numberOfAccounts, 0)
+ assert.equal(output.mnemonic, null)
+ })
+ })
+ })
+
+ describe('#deserialize a private key', function() {
+ it('serializes what it deserializes', function(done) {
+ keyring.deserialize({
+ mnemonic: sampleMnemonic,
+ numberOfAccounts: 1
+ })
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1, 'restores two accounts')
+ return keyring.addAccounts(1)
+ }).then(() => {
+ return keyring.getAccounts()
+ }).then((accounts) => {
+ assert.equal(accounts[0], firstAcct)
+ assert.equal(accounts[1], secondAcct)
+ assert.equal(accounts.length, 2)
+
+ return keyring.serialize()
+ }).then((serialized) => {
+ assert.equal(serialized.mnemonic, sampleMnemonic)
+ done()
+ })
+ })
+ })
+
+ describe('#addAccounts', function() {
+ describe('with no arguments', function() {
+ it('creates a single wallet', function(done) {
+ keyring.addAccounts()
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1)
+ done()
+ })
+ })
+ })
+
+ describe('with a numeric argument', function() {
+ it('creates that number of wallets', function(done) {
+ keyring.addAccounts(3)
+ .then(() => {
+ assert.equal(keyring.wallets.length, 3)
+ done()
+ })
+ })
+ })
+ })
+
+ describe('#getAccounts', function() {
+ it('calls getAddress on each wallet', function(done) {
+
+ // Push a mock wallet
+ const desiredOutput = 'foo'
+ keyring.wallets.push({
+ getAddress() {
+ return {
+ toString() {
+ return desiredOutput
+ }
+ }
+ }
+ })
+
+ const output = keyring.getAccounts()
+ .then((output) => {
+ assert.equal(output[0], desiredOutput)
+ assert.equal(output.length, 1)
+ done()
+ })
+ })
+ })
+})
diff --git a/test/unit/keyrings/simple-test.js b/test/unit/keyrings/simple-test.js
new file mode 100644
index 000000000..ba7dd448a
--- /dev/null
+++ b/test/unit/keyrings/simple-test.js
@@ -0,0 +1,149 @@
+const assert = require('assert')
+const extend = require('xtend')
+const Web3 = require('web3')
+const web3 = new Web3()
+const ethUtil = require('ethereumjs-util')
+const SimpleKeyring = require('../../../app/scripts/keyrings/simple')
+const TYPE_STR = 'Simple Key Pair'
+
+// Sample account:
+const privKeyHex = 'b8a9c05beeedb25df85f8d641538cbffedf67216048de9c678ee26260eb91952'
+
+describe('simple-keyring', function() {
+
+ let keyring
+ beforeEach(function() {
+ keyring = new SimpleKeyring()
+ })
+
+ describe('Keyring.type', function() {
+ it('is a class property that returns the type string.', function() {
+ const type = SimpleKeyring.type
+ assert.equal(type, TYPE_STR)
+ })
+ })
+
+ describe('#type', function() {
+ it('returns the correct value', function() {
+ const type = keyring.type
+ assert.equal(type, TYPE_STR)
+ })
+ })
+
+ describe('#serialize empty wallets.', function() {
+ it('serializes an empty array', function(done) {
+ keyring.serialize()
+ .then((output) => {
+ assert.deepEqual(output, [])
+ done()
+ })
+ })
+ })
+
+ describe('#deserialize a private key', function() {
+ it('serializes what it deserializes', function() {
+ keyring.deserialize([privKeyHex])
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1, 'has one wallet')
+ const serialized = keyring.serialize()
+ assert.equal(serialized[0], privKeyHex)
+ })
+ })
+ })
+
+ describe('#signMessage', function() {
+ const address = '0x9858e7d8b79fc3e6d989636721584498926da38a'
+ const message = '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'
+ const privateKey = '0x7dd98753d7b4394095de7d176c58128e2ed6ee600abe97c9f6d9fd65015d9b18'
+ const expectedResult = '0x28fcb6768e5110144a55b2e6ce9d1ea5a58103033632d272d2b5cf506906f7941a00b539383fd872109633d8c71c404e13dba87bc84166ee31b0e36061a69e161c'
+
+ it('passes the dennis test', function(done) {
+ keyring.deserialize([ privateKey ])
+ .then(() => {
+ return keyring.signMessage(address, message)
+ })
+ .then((result) => {
+ assert.equal(result, expectedResult)
+ done()
+ })
+ })
+
+ it('reliably can decode messages it signs', function (done) {
+
+ const message = 'hello there!'
+ const msgHashHex = web3.sha3(message)
+ let address
+ let addresses = []
+
+ keyring.deserialize([ privateKey ])
+ .then(() => {
+ keyring.addAccounts(9)
+ })
+ .then(() => {
+ return keyring.getAccounts()
+ })
+ .then((addrs) => {
+ addresses = addrs
+ return Promise.all(addresses.map((address) => {
+ return keyring.signMessage(address, msgHashHex)
+ }))
+ })
+ .then((signatures) => {
+
+ signatures.forEach((sgn, index) => {
+ const address = addresses[index]
+
+ var r = ethUtil.toBuffer(sgn.slice(0,66))
+ var s = ethUtil.toBuffer('0x' + sgn.slice(66,130))
+ var v = ethUtil.bufferToInt(ethUtil.toBuffer('0x' + sgn.slice(130,132)))
+ var m = ethUtil.toBuffer(msgHashHex)
+ var pub = ethUtil.ecrecover(m, v, r, s)
+ var adr = '0x' + ethUtil.pubToAddress(pub).toString('hex')
+
+ assert.equal(adr, address, 'recovers address from signature correctly')
+ })
+ done()
+ })
+ })
+ })
+
+ describe('#addAccounts', function() {
+ describe('with no arguments', function() {
+ it('creates a single wallet', function() {
+ keyring.addAccounts()
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1)
+ })
+ })
+ })
+
+ describe('with a numeric argument', function() {
+ it('creates that number of wallets', function() {
+ keyring.addAccounts(3)
+ .then(() => {
+ assert.equal(keyring.wallets.length, 3)
+ })
+ })
+ })
+ })
+
+ describe('#getAccounts', function() {
+ it('calls getAddress on each wallet', function(done) {
+
+ // Push a mock wallet
+ const desiredOutput = '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761'
+ keyring.wallets.push({
+ getAddress() {
+ return ethUtil.toBuffer(desiredOutput)
+ }
+ })
+
+ keyring.getAccounts()
+ .then((output) => {
+ assert.equal(output[0], desiredOutput)
+ assert.equal(output.length, 1)
+ done()
+ })
+ })
+ })
+})
diff --git a/test/unit/message-manager-test.js b/test/unit/message-manager-test.js
new file mode 100644
index 000000000..faf7429d4
--- /dev/null
+++ b/test/unit/message-manager-test.js
@@ -0,0 +1,89 @@
+const assert = require('assert')
+const extend = require('xtend')
+const EventEmitter = require('events')
+
+const MessageManger = require('../../app/scripts/lib/message-manager')
+
+describe('Transaction Manager', function() {
+ let messageManager
+
+ beforeEach(function() {
+ messageManager = new MessageManger ()
+ })
+
+ describe('#getMsgList', function() {
+ it('when new should return empty array', function() {
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ it('should also return transactions from local storage if any', function() {
+
+ })
+ })
+
+ describe('#addMsg', function() {
+ it('adds a Msg returned in getMsgList', function() {
+ var Msg = { id: 1, status: 'approved', metamaskNetworkId: 'unit test' }
+ messageManager.addMsg(Msg)
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].id, 1)
+ })
+ })
+
+ describe('#setMsgStatusApproved', function() {
+ it('sets the Msg status to approved', function() {
+ var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
+ messageManager.addMsg(Msg)
+ messageManager.setMsgStatusApproved(1)
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'approved')
+ })
+ })
+
+ describe('#rejectMsg', function() {
+ it('sets the Msg status to rejected', function() {
+ var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
+ messageManager.addMsg(Msg)
+ messageManager.rejectMsg(1)
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'rejected')
+ })
+ })
+
+ describe('#_updateMsg', function() {
+ it('replaces the Msg with the same id', function() {
+ messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
+ messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
+ messageManager._updateMsg({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test' })
+ var result = messageManager.getMsg('1')
+ assert.equal(result.hash, 'foo')
+ })
+ })
+
+ describe('#getUnapprovedMsgs', function() {
+ it('returns unapproved Msgs in a hash', function() {
+ messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
+ messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
+ let result = messageManager.getUnapprovedMsgs()
+ assert.equal(typeof result, 'object')
+ assert.equal(result['1'].status, 'unapproved')
+ assert.equal(result['2'], undefined)
+ })
+ })
+
+ describe('#getMsg', function() {
+ it('returns a Msg with the requested id', function() {
+ messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
+ messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
+ assert.equal(messageManager.getMsg('1').status, 'unapproved')
+ assert.equal(messageManager.getMsg('2').status, 'approved')
+ })
+ })
+})
diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js
new file mode 100644
index 000000000..78b9e9df7
--- /dev/null
+++ b/test/unit/metamask-controller-test.js
@@ -0,0 +1,29 @@
+const assert = require('assert')
+const sinon = require('sinon')
+const clone = require('clone')
+const MetaMaskController = require('../../app/scripts/metamask-controller')
+const firstTimeState = require('../../app/scripts/first-time-state')
+
+const STORAGE_KEY = 'metamask-config'
+
+describe('MetaMaskController', function() {
+ const noop = () => {}
+ let controller = new MetaMaskController({
+ showUnconfirmedMessage: noop,
+ unlockAccountMessage: noop,
+ showUnapprovedTx: noop,
+ // initial state
+ initState: clone(firstTimeState),
+ })
+
+ beforeEach(function() {
+ // sinon allows stubbing methods that are easily verified
+ this.sinon = sinon.sandbox.create()
+ })
+
+ afterEach(function() {
+ // sinon requires cleanup otherwise it will overwrite context
+ this.sinon.restore()
+ })
+
+}) \ No newline at end of file
diff --git a/test/unit/migrations-test.js b/test/unit/migrations-test.js
index 9ea8d5c5a..d2a83be77 100644
--- a/test/unit/migrations-test.js
+++ b/test/unit/migrations-test.js
@@ -1,34 +1,97 @@
-var assert = require('assert')
-var path = require('path')
+const assert = require('assert')
+const path = require('path')
-var wallet1 = require(path.join('..', 'lib', 'migrations', '001.json'))
+const wallet1 = require(path.join('..', 'lib', 'migrations', '001.json'))
+const vault4 = require(path.join('..', 'lib', 'migrations', '004.json'))
+let vault5, vault6, vault7, vault8, vault9, vault10, vault11
-var migration2 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '002'))
-var migration3 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '003'))
-var migration4 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '004'))
+const migration2 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '002'))
+const migration3 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '003'))
+const migration4 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '004'))
+const migration5 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '005'))
+const migration6 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '006'))
+const migration7 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '007'))
+const migration8 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '008'))
+const migration9 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '009'))
+const migration10 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '010'))
+const migration11 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '011'))
-describe('wallet1 is migrated successfully', function() {
+const oldTestRpc = 'https://rawtestrpc.metamask.io/'
+const newTestRpc = 'https://testrpc.metamask.io/'
- it('should convert providers', function(done) {
+describe('wallet1 is migrated successfully', () => {
+ it('should convert providers', () => {
wallet1.data.config.provider = { type: 'etherscan', rpcTarget: null }
- var firstResult = migration2.migrate(wallet1.data)
- assert.equal(firstResult.config.provider.type, 'rpc', 'provider should be rpc')
- assert.equal(firstResult.config.provider.rpcTarget, 'https://rpc.metamask.io/', 'main provider should be our rpc')
+ return migration2.migrate(wallet1)
+ .then((secondResult) => {
+ const secondData = secondResult.data
+ assert.equal(secondData.config.provider.type, 'rpc', 'provider should be rpc')
+ assert.equal(secondData.config.provider.rpcTarget, 'https://rpc.metamask.io/', 'main provider should be our rpc')
+ secondResult.data.config.provider.rpcTarget = oldTestRpc
+ return migration3.migrate(secondResult)
+ }).then((thirdResult) => {
+ assert.equal(thirdResult.data.config.provider.rpcTarget, newTestRpc, 'config.provider.rpcTarget should be set to the proper testrpc url.')
+ return migration4.migrate(thirdResult)
+ }).then((fourthResult) => {
+ const fourthData = fourthResult.data
+ assert.equal(fourthData.config.provider.rpcTarget, null, 'old rpcTarget should not exist.')
+ assert.equal(fourthData.config.provider.type, 'testnet', 'config.provider should be set to testnet.')
- var oldTestRpc = 'https://rawtestrpc.metamask.io/'
- var newTestRpc = 'https://testrpc.metamask.io/'
- firstResult.config.provider.rpcTarget = oldTestRpc
+ return migration5.migrate(vault4)
+ }).then((fifthResult) => {
+ const fifthData = fifthResult.data
+ assert.equal(fifthData.vault, null, 'old vault should not exist')
+ assert.equal(fifthData.walletNicknames, null, 'old walletNicknames should not exist')
+ assert.equal(fifthData.config.selectedAccount, null, 'old config.selectedAccount should not exist')
+ assert.equal(fifthData.KeyringController.vault, vault4.data.vault, 'KeyringController.vault should exist')
+ assert.equal(fifthData.KeyringController.selectedAccount, vault4.data.config.selectedAccount, 'KeyringController.selectedAccount should have moved')
+ assert.equal(fifthData.KeyringController.walletNicknames['0x0beb674745816b125fbc07285d39fd373e64895c'], vault4.data.walletNicknames['0x0beb674745816b125fbc07285d39fd373e64895c'], 'KeyringController.walletNicknames should have moved')
- var secondResult = migration3.migrate(firstResult)
- assert.equal(secondResult.config.provider.rpcTarget, newTestRpc)
+ vault5 = fifthResult
+ return migration6.migrate(fifthResult)
+ }).then((sixthResult) => {
+ assert.equal(sixthResult.data.KeyringController.selectedAccount, null, 'old selectedAccount should not exist')
+ assert.equal(sixthResult.data.PreferencesController.selectedAddress, vault5.data.KeyringController.selectedAccount, 'selectedAccount should have moved')
- var thirdResult = migration4.migrate(secondResult)
- assert.equal(secondResult.config.provider.rpcTarget, null)
- assert.equal(secondResult.config.provider.type, 'testnet')
+ vault6 = sixthResult
+ return migration7.migrate(sixthResult)
+ }).then((seventhResult) => {
+ assert.equal(seventhResult.data.transactions, null, 'old transactions should not exist')
+ assert.equal(seventhResult.data.gasMultiplier, null, 'old gasMultiplier should not exist')
+ assert.equal(seventhResult.data.TransactionManager.transactions[0].id, vault6.data.transactions[0].id, 'transactions should have moved')
+ assert.equal(seventhResult.data.TransactionManager.gasMultiplier, vault6.data.gasMultiplier, 'gasMultiplier should have moved')
+
+ vault7 = seventhResult
+ return migration8.migrate(seventhResult)
+ }).then((eighthResult) => {
+ assert.equal(eighthResult.data.noticesList, null, 'old noticesList should not exist')
+ assert.equal(eighthResult.data.NoticeController.noticesList[0].title, vault7.data.noticesList[0].title, 'noticesList should have moved')
+
+ vault8 = eighthResult
+ return migration9.migrate(eighthResult)
+ }).then((ninthResult) => {
+ assert.equal(ninthResult.data.currentFiat, null, 'old currentFiat should not exist')
+ assert.equal(ninthResult.data.fiatCurrency, null, 'old fiatCurrency should not exist')
+ assert.equal(ninthResult.data.conversionRate, null, 'old conversionRate should not exist')
+ assert.equal(ninthResult.data.conversionDate, null, 'old conversionDate should not exist')
+
+ assert.equal(ninthResult.data.CurrencyController.currentCurrency, vault8.data.fiatCurrency, 'currentFiat should have moved')
+ assert.equal(ninthResult.data.CurrencyController.conversionRate, vault8.data.conversionRate, 'conversionRate should have moved')
+ assert.equal(ninthResult.data.CurrencyController.conversionDate, vault8.data.conversionDate, 'conversionDate should have moved')
+
+ vault9 = ninthResult
+ return migration10.migrate(ninthResult)
+ }).then((tenthResult) => {
+ assert.equal(tenthResult.data.shapeShiftTxList, null, 'old shapeShiftTxList should not exist')
+ assert.equal(tenthResult.data.ShapeShiftController.shapeShiftTxList[0].transaction, vault9.data.shapeShiftTxList[0].transaction)
+
+ return migration11.migrate(tenthResult)
+ }).then((eleventhResult) => {
+ assert.equal(eleventhResult.data.isDisclaimerConfirmed, null, 'isDisclaimerConfirmed should not exist')
+ assert.equal(eleventhResult.data.TOSHash, null, 'TOSHash should not exist')
+ })
- done()
})
})
-
diff --git a/test/unit/nodeify-test.js b/test/unit/nodeify-test.js
new file mode 100644
index 000000000..a14d34338
--- /dev/null
+++ b/test/unit/nodeify-test.js
@@ -0,0 +1,22 @@
+const assert = require('assert')
+const nodeify = require('../../app/scripts/lib/nodeify')
+
+describe('nodeify', function() {
+
+ var obj = {
+ foo: 'bar',
+ promiseFunc: function (a) {
+ var solution = this.foo + a
+ return Promise.resolve(solution)
+ }
+ }
+
+ it('should retain original context', function(done) {
+ var nodified = nodeify(obj.promiseFunc).bind(obj)
+ nodified('baz', function (err, res) {
+ assert.equal(res, 'barbaz')
+ done()
+ })
+ })
+
+})
diff --git a/test/unit/notice-controller-test.js b/test/unit/notice-controller-test.js
new file mode 100644
index 000000000..cf00daeba
--- /dev/null
+++ b/test/unit/notice-controller-test.js
@@ -0,0 +1,116 @@
+const assert = require('assert')
+const extend = require('xtend')
+const rp = require('request-promise')
+const nock = require('nock')
+const configManagerGen = require('../lib/mock-config-manager')
+const NoticeController = require('../../app/scripts/notice-controller')
+const STORAGE_KEY = 'metamask-persistance-key'
+
+describe('notice-controller', function() {
+ var noticeController
+
+ beforeEach(function() {
+ // simple localStorage polyfill
+ window.localStorage = {}
+ if (window.localStorage.clear) window.localStorage.clear()
+ let configManager = configManagerGen()
+ noticeController = new NoticeController({
+ configManager: configManager,
+ })
+ })
+
+ describe('notices', function() {
+ describe('#getNoticesList', function() {
+ it('should return an empty array when new', function() {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ var result = noticeController.getNoticesList()
+ assert.equal(result.length, 0)
+ })
+ })
+
+ describe('#setNoticesList', function() {
+ it('should set data appropriately', function () {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ var testListId = noticeController.getNoticesList()[0].id
+ assert.equal(testListId, 0)
+ })
+ })
+
+ describe('#updateNoticeslist', function() {
+ it('should integrate the latest changes from the source', function() {
+ var testList = [{
+ id:55,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ noticeController.updateNoticesList().then(() => {
+ var newList = noticeController.getNoticesList()
+ assert.ok(newList[0].id === 55)
+ assert.ok(newList[1])
+ })
+ })
+ it('should not overwrite any existing fields', function () {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ noticeController.updateNoticesList().then(() => {
+ var newList = noticeController.getNoticesList()
+ assert.equal(newList[0].id, 0)
+ assert.equal(newList[0].title, "Futuristic Notice")
+ assert.equal(newList.length, 1)
+ })
+ })
+ })
+
+ describe('#markNoticeRead', function () {
+ it('should mark a notice as read', function () {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ noticeController.markNoticeRead(testList[0])
+ var newList = noticeController.getNoticesList()
+ assert.ok(newList[0].read)
+ })
+ })
+
+ describe('#getLatestUnreadNotice', function () {
+ it('should retrieve the latest unread notice', function () {
+ var testList = [
+ {id:0,read:true,title:"Past Notice"},
+ {id:1,read:false,title:"Current Notice"},
+ {id:2,read:false,title:"Future Notice"},
+ ]
+ noticeController.setNoticesList(testList)
+ var latestUnread = noticeController.getLatestUnreadNotice()
+ assert.equal(latestUnread.id, 2)
+ })
+ it('should return undefined if no unread notices exist.', function () {
+ var testList = [
+ {id:0,read:true,title:"Past Notice"},
+ {id:1,read:true,title:"Current Notice"},
+ {id:2,read:true,title:"Future Notice"},
+ ]
+ noticeController.setNoticesList(testList)
+ var latestUnread = noticeController.getLatestUnreadNotice()
+ assert.ok(!latestUnread)
+ })
+ })
+ })
+
+})
diff --git a/test/unit/tx-manager-test.js b/test/unit/tx-manager-test.js
new file mode 100644
index 000000000..f64f048e3
--- /dev/null
+++ b/test/unit/tx-manager-test.js
@@ -0,0 +1,211 @@
+const assert = require('assert')
+const extend = require('xtend')
+const EventEmitter = require('events')
+const ObservableStore = require('obs-store')
+const STORAGE_KEY = 'metamask-persistance-key'
+const TransactionManager = require('../../app/scripts/transaction-manager')
+const noop = () => true
+
+describe('Transaction Manager', function() {
+ let txManager
+
+ beforeEach(function() {
+ txManager = new TransactionManager({
+ networkStore: new ObservableStore({ network: 'unit test' }),
+ txHistoryLimit: 10,
+ blockTracker: new EventEmitter(),
+ })
+ })
+
+ describe('#validateTxParams', function () {
+ it('returns null for positive values', function() {
+ var sample = {
+ value: '0x01'
+ }
+ var res = txManager.txProviderUtils.validateTxParams(sample, (err) => {
+ assert.equal(err, null, 'no error')
+ })
+ })
+
+
+ it('returns error for negative values', function() {
+ var sample = {
+ value: '-0x01'
+ }
+ var res = txManager.txProviderUtils.validateTxParams(sample, (err) => {
+ assert.ok(err, 'error')
+ })
+ })
+ })
+
+ describe('#getTxList', function() {
+ it('when new should return empty array', function() {
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ it('should also return transactions from local storage if any', function() {
+
+ })
+ })
+
+ describe('#addTx', function() {
+ it('adds a tx returned in getTxList', function() {
+ var tx = { id: 1, status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].id, 1)
+ })
+
+ it('cuts off early txs beyond a limit', function() {
+ const limit = txManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ }
+ var result = txManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 1, 'early txs truncted')
+ })
+
+ it('cuts off early txs beyond a limit whether or not it is confirmed or rejected', function() {
+ const limit = txManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ let tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ }
+ var result = txManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 1, 'early txs truncted')
+ })
+
+ it('cuts off early txs beyond a limit but does not cut unapproved txs', function() {
+ var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(unconfirmedTx, noop)
+ const limit = txManager.txHistoryLimit
+ for (let i = 1; i < limit + 1; i++) {
+ let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ }
+ var result = txManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 0, 'first tx should still be there')
+ assert.equal(result[0].status, 'unapproved', 'first tx should be unapproved')
+ assert.equal(result[1].id, 2, 'early txs truncted')
+ })
+ })
+
+ describe('#setTxStatusSigned', function() {
+ it('sets the tx status to signed', function() {
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ txManager.setTxStatusSigned(1)
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'signed')
+ })
+
+ it('should emit a signed event to signal the exciton of callback', (done) => {
+ this.timeout(10000)
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ let noop = function () {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txManager.addTx(tx)
+ txManager.on('1:signed', noop)
+ txManager.setTxStatusSigned(1)
+ })
+ })
+
+ describe('#setTxStatusRejected', function() {
+ it('sets the tx status to rejected', function() {
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx)
+ txManager.setTxStatusRejected(1)
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'rejected')
+ })
+
+ it('should emit a rejected event to signal the exciton of callback', (done) => {
+ this.timeout(10000)
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx)
+ let noop = function (err, txId) {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txManager.on('1:rejected', noop)
+ txManager.setTxStatusRejected(1)
+ })
+
+ })
+
+ describe('#updateTx', function() {
+ it('replaces the tx with the same id', function() {
+ txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test', txParams: {} })
+ var result = txManager.getTx('1')
+ assert.equal(result.hash, 'foo')
+ })
+ })
+
+ describe('#getUnapprovedTxList', function() {
+ it('returns unapproved txs in a hash', function() {
+ txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ let result = txManager.getUnapprovedTxList()
+ assert.equal(typeof result, 'object')
+ assert.equal(result['1'].status, 'unapproved')
+ assert.equal(result['2'], undefined)
+ })
+ })
+
+ describe('#getTx', function() {
+ it('returns a tx with the requested id', function() {
+ txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ assert.equal(txManager.getTx('1').status, 'unapproved')
+ assert.equal(txManager.getTx('2').status, 'confirmed')
+ })
+ })
+
+ describe('#getFilteredTxList', function() {
+ it('returns a tx with the requested data', function() {
+ let txMetas = [
+ { id: 0, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 1, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 2, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 3, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 4, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 5, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 6, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 7, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ ]
+ txMetas.forEach((txMeta) => txManager.addTx(txMeta, noop))
+ let filterParams
+
+ filterParams = { status: 'unapproved', from: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'unapproved', to: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 2, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed', from: '0xbb' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { from: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { to: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ })
+ })
+
+})
diff --git a/test/unit/util_test.js b/test/unit/util_test.js
index 45e545e8e..00528b905 100644
--- a/test/unit/util_test.js
+++ b/test/unit/util_test.js
@@ -227,5 +227,27 @@ describe('util', function() {
assert.equal(result.toString(10), '1111000000000000000', 'accepts decimals')
})
})
+ describe('#isHex', function(){
+ it('should return true when given a hex string', function() {
+ var result = util.isHex('c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2')
+ assert(result)
+ })
+
+ it('should return false when given a non-hex string', function() {
+ var result = util.isHex('c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714imnotreal')
+ assert(!result)
+ })
+
+ it('should return false when given a string containing a non letter/number character', function() {
+ var result = util.isHex('c3ab8ff13720!8ad9047dd39466b3c%8974e592c2fa383d4a396071imnotreal')
+ assert(!result)
+ })
+
+ it('should return true when given a hex string with hex-prefix', function() {
+ var result = util.isHex('0xc3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2')
+ assert(result)
+ })
+
+ })
})
})