aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/integration/helpers.js4
-rw-r--r--test/integration/index.html4
-rw-r--r--test/integration/index.js21
-rw-r--r--test/integration/lib/encryptor-test.js71
-rw-r--r--test/integration/lib/first-time.js90
-rw-r--r--test/integration/lib/keyring-controller-test.js62
-rw-r--r--test/integration/mocks/oldVault.json21
-rw-r--r--test/integration/tests.js24
-rw-r--r--test/lib/mock-config-manager.js2
-rw-r--r--test/lib/mock-encryptor.js32
-rw-r--r--test/lib/mock-simple-keychain.js38
-rw-r--r--test/unit/actions/restore_vault_test.js60
-rw-r--r--test/unit/actions/set_selected_account_test.js1
-rw-r--r--test/unit/actions/tx_test.js8
-rw-r--r--test/unit/config-manager-test.js28
-rw-r--r--test/unit/idStore-migration-test.js160
-rw-r--r--test/unit/idStore-test.js3
-rw-r--r--test/unit/keyring-controller-test.js200
-rw-r--r--test/unit/keyrings/hd-test.js127
-rw-r--r--test/unit/keyrings/simple-test.js94
-rw-r--r--test/unit/nodeify-test.js22
21 files changed, 962 insertions, 110 deletions
diff --git a/test/integration/helpers.js b/test/integration/helpers.js
index 95c36017a..eede103b4 100644
--- a/test/integration/helpers.js
+++ b/test/integration/helpers.js
@@ -1,7 +1,7 @@
-function wait() {
+function wait(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve()
- }, 500)
+ }, time * 3 || 1500)
})
}
diff --git a/test/integration/index.html b/test/integration/index.html
index 6de40b046..8a54cb829 100644
--- a/test/integration/index.html
+++ b/test/integration/index.html
@@ -12,10 +12,10 @@
<script src="https://code.jquery.com/qunit/qunit-2.0.0.js"></script>
<script src="./jquery-3.1.0.min.js"></script>
<script src="helpers.js"></script>
- <script src="tests.js"></script>
+ <script src="bundle.js"></script>
<script src="/testem.js"></script>
- <iframe src="/development/index.html" height="500px" width="360px">
+ <iframe src="/development/test.html" height="500px" width="360px">
<p>Your browser does not support iframes</p>
</iframe>
</body>
diff --git a/test/integration/index.js b/test/integration/index.js
new file mode 100644
index 000000000..ff6d1baf8
--- /dev/null
+++ b/test/integration/index.js
@@ -0,0 +1,21 @@
+var fs = require('fs')
+var path = require('path')
+var browserify = require('browserify');
+var tests = fs.readdirSync(path.join(__dirname, 'lib'))
+var bundlePath = path.join(__dirname, 'bundle.js')
+
+var b = browserify();
+
+// Remove old bundle
+try {
+ fs.unlinkSync(bundlePath)
+} catch (e) {}
+
+var writeStream = fs.createWriteStream(bundlePath)
+
+tests.forEach(function(fileName) {
+ b.add(path.join(__dirname, 'lib', fileName))
+})
+
+b.bundle().pipe(writeStream);
+
diff --git a/test/integration/lib/encryptor-test.js b/test/integration/lib/encryptor-test.js
new file mode 100644
index 000000000..897d22740
--- /dev/null
+++ b/test/integration/lib/encryptor-test.js
@@ -0,0 +1,71 @@
+var encryptor = require('../../../app/scripts/lib/encryptor')
+
+QUnit.module('encryptor')
+
+QUnit.test('encryptor:serializeBufferForStorage', function (assert) {
+ assert.expect(1)
+ var buf = new Buffer(2)
+ buf[0] = 16
+ buf[1] = 1
+
+ var output = encryptor.serializeBufferForStorage(buf)
+
+ var expect = '0x1001'
+ assert.equal(expect, output)
+})
+
+QUnit.test('encryptor:serializeBufferFromStorage', function (assert) {
+ assert.expect(2)
+ var input = '0x1001'
+ var output = encryptor.serializeBufferFromStorage(input)
+
+ assert.equal(output[0], 16)
+ assert.equal(output[1], 1)
+})
+
+QUnit.test('encryptor:encrypt & decrypt', function(assert) {
+ var done = assert.async();
+ var password, data, encrypted
+
+ password = 'a sample passw0rd'
+ data = { foo: 'data to encrypt' }
+
+ encryptor.encrypt(password, data)
+ .then(function(encryptedStr) {
+ assert.equal(typeof encryptedStr, 'string', 'returns a string')
+ return encryptor.decrypt(password, encryptedStr)
+ })
+ .then(function (decryptedObj) {
+ assert.deepEqual(decryptedObj, data, 'decrypted what was encrypted')
+ done()
+ })
+ .catch(function(reason) {
+ assert.ifError(reason, 'threw an error')
+ done(reason)
+ })
+
+})
+
+QUnit.test('encryptor:encrypt & decrypt with wrong password', function(assert) {
+ var done = assert.async();
+ var password, data, encrypted, wrongPassword
+
+ password = 'a sample passw0rd'
+ wrongPassword = 'a wrong password'
+ data = { foo: 'data to encrypt' }
+
+ encryptor.encrypt(password, data)
+ .then(function(encryptedStr) {
+ assert.equal(typeof encryptedStr, 'string', 'returns a string')
+ return encryptor.decrypt(wrongPassword, encryptedStr)
+ })
+ .then(function (decryptedObj) {
+ assert.equal(!decryptedObj, true, 'Wrong password should not decrypt')
+ done()
+ })
+ .catch(function(reason) {
+ done()
+ })
+})
+
+
diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js
new file mode 100644
index 000000000..12c573db1
--- /dev/null
+++ b/test/integration/lib/first-time.js
@@ -0,0 +1,90 @@
+const PASSWORD = 'password123'
+
+QUnit.module('first time usage')
+
+QUnit.test('agree to terms', function (assert) {
+ var done = assert.async()
+ let app
+
+ wait().then(function() {
+ app = $('iframe').contents().find('#app-content .mock-app-root')
+ app.find('.markdown').prop('scrollTop', 100000000)
+ return wait()
+
+ }).then(function() {
+
+ var title = app.find('h1').text()
+ assert.equal(title, 'MetaMask', 'title screen')
+
+ var pwBox = app.find('#password-box')[0]
+ var confBox = app.find('#password-box-confirm')[0]
+
+ pwBox.value = PASSWORD
+ confBox.value = PASSWORD
+ return wait()
+
+ }).then(function() {
+
+ var createButton = app.find('button.primary')[0]
+ createButton.click()
+
+ return wait(1500)
+ }).then(function() {
+
+ var terms = app.find('h3.terms-header')[0]
+ assert.equal(terms.textContent, 'MetaMask Terms & Conditions', 'Showing TOS')
+
+ // Scroll through terms
+ var scrollable = app.find('.markdown')[0]
+ scrollable.scrollTop = scrollable.scrollHeight
+
+ return wait(10)
+ }).then(function() {
+
+ var button = app.find('button')[0] // Agree button
+ button.click()
+
+ return wait(1000)
+ }).then(function() {
+
+ var created = app.find('h3')[0]
+ assert.equal(created.textContent, 'Vault Created', 'Vault created screen')
+
+ var button = app.find('button')[0] // Agree button
+ button.click()
+
+ return wait(1000)
+ }).then(function() {
+
+ var detail = app.find('.account-detail-section')[0]
+ assert.ok(detail, 'Account detail section loaded.')
+
+ var sandwich = app.find('.sandwich-expando')[0]
+ sandwich.click()
+
+ return wait()
+ }).then(function() {
+
+ var sandwich = app.find('.menu-droppo')[0]
+ var lock = sandwich.children[2]
+ assert.ok(lock, 'Lock menu item found')
+ lock.click()
+
+ return wait(1000)
+ }).then(function() {
+
+ var pwBox = app.find('#password-box')[0]
+ pwBox.value = PASSWORD
+
+ var createButton = app.find('button.primary')[0]
+ createButton.click()
+
+ return wait(1500)
+ }).then(function() {
+
+ var detail = app.find('.account-detail-section')[0]
+ assert.ok(detail, 'Account detail section loaded again.')
+
+ done()
+ })
+})
diff --git a/test/integration/lib/keyring-controller-test.js b/test/integration/lib/keyring-controller-test.js
new file mode 100644
index 000000000..ae5ecc578
--- /dev/null
+++ b/test/integration/lib/keyring-controller-test.js
@@ -0,0 +1,62 @@
+var KeyringController = require('../../../app/scripts/keyring-controller')
+var ConfigManager = require('../../../app/scripts/lib/config-manager')
+
+var oldStyleVault = require('../mocks/oldVault.json')
+
+var STORAGE_KEY = 'metamask-config'
+var PASSWORD = '12345678'
+var FIRST_ADDRESS = '0x4dd5d356c5A016A220bCD69e82e5AF680a430d00'.toLowerCase()
+
+
+QUnit.module('Old Style Vaults', {
+ beforeEach: function () {
+ window.localStorage[STORAGE_KEY] = JSON.stringify(oldStyleVault)
+
+ this.configManager = new ConfigManager({
+ loadData: () => { return JSON.parse(window.localStorage[STORAGE_KEY]) },
+ setData: (data) => { window.localStorage[STORAGE_KEY] = JSON.stringify(data) },
+ })
+
+ this.keyringController = new KeyringController({
+ configManager: this.configManager,
+ getNetwork: () => { return '2' },
+ })
+
+ this.ethStore = {
+ addAccount: () => {},
+ removeAccount: () => {},
+ }
+
+ this.keyringController.setStore(this.ethStore)
+ }
+})
+
+QUnit.test('keyringController:isInitialized', function (assert) {
+ assert.ok(this.keyringController.getState().isInitialized)
+})
+
+QUnit.test('keyringController:submitPassword', function (assert) {
+ var done = assert.async()
+
+ this.keyringController.submitPassword(PASSWORD)
+ .then((state) => {
+ assert.ok(state.identities[FIRST_ADDRESS])
+ done()
+ })
+})
+
+QUnit.test('keyringController:setLocked', function (assert) {
+ var done = assert.async()
+ var self = this
+
+ this.keyringController.setLocked()
+ .then(function() {
+ assert.notOk(self.keyringController.password, 'password should be deallocated')
+ assert.deepEqual(self.keyringController.keyrings, [], 'keyrings should be deallocated')
+ done()
+ })
+ .catch((reason) => {
+ assert.ifError(reason)
+ done()
+ })
+})
diff --git a/test/integration/mocks/oldVault.json b/test/integration/mocks/oldVault.json
new file mode 100644
index 000000000..5861c41d7
--- /dev/null
+++ b/test/integration/mocks/oldVault.json
@@ -0,0 +1,21 @@
+{
+ "meta": {
+ "version": 4
+ },
+ "data": {
+ "fiatCurrency": "USD",
+ "isConfirmed": true,
+ "TOSHash": "a4f4e23f823a7ac51783e7ffba7914a911b09acdb97263296b7e14b527f80c5b",
+ "conversionRate": 9.47316629,
+ "conversionDate": 1479510994,
+ "wallet": "{\"encSeed\":{\"encStr\":\"a5tjKtDGlHkua+6Ta5s3wMFWPmsBqaPdMKGmqeI2z1kMbNs3V03HBaCptU7NtMra1DjHKbSNsUToxFUrmrvWBmUejamN16+l1CviwqASsv7kKzpot00/dfyyJgtZwwFP5Je+TAB1V231nRbPidOfeE1cDec5V8KTF8epl6qzsbA25pjeW76Dfw==\",\"nonce\":\"RzID6bAhWfGTSR74xdIh3RaT1+1sLk6F\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"6nlYAopRbmGcqerRZO08XwgeYaCJg9XRhh4oiYiVVdQtyNPdxvOI9TcE/mqvBiatMwBwA+TmsqTV6eZZe/VDZKYIGajKulQbScd0xQ71JhYfqqmzSG6EH2Pnzwa+aSAsfARgN1JJSaff2+p6wV6Zg5BUDtl72OGEIEfXhcUGwg==\",\"nonce\":\"Ee1KiDqtx7NvYToQUFvjEhKNinNQcXlK\"},\"hdIndex\":1,\"encPrivKeys\":{\"4dd5d356c5a016a220bcd69e82e5af680a430d00\":{\"key\":\"htGRGAH10lGF4M+fvioznmYVIUSWAzwp/yWSIo85psgZZwmCdJY72oyGanYsrFO8\",\"nonce\":\"PkP8XeZ+ok215rzEorvJu9nYTWzkOVr0\"}},\"addresses\":[\"4dd5d356c5a016a220bcd69e82e5af680a430d00\"]}},\"encHdRootPriv\":{\"encStr\":\"TAZAo71a+4IlAaoA66f0w4ts2f+V7ArTSUHRIrMltfAPXz7GfJBmKXNtHPORUYAjRiKqWK6FZnhKLf7Vcng2LG7VnDQwC4xPxzSRZzSEilnoY3V+zRY0HD7Wb/pndb4FliA/buZQmjohO4vezeX0hl70rJlPJEZTyYoWgxbxFA==\",\"nonce\":\"FlJOaLyBEHMaH5fEnYjdHc6nn18+WkRj\"},\"salt\":\"CmuCcWpbqpKUUv+1aE2ZwvQl7EIQ731uFibSq++vwtY=\",\"version\":2}",
+ "config": {
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAccount": "0x4dd5d356c5a016a220bcd69e82e5af680a430d00"
+ },
+ "showSeedWords": false,
+ "isEthConfirmed": true
+ }
+}
diff --git a/test/integration/tests.js b/test/integration/tests.js
deleted file mode 100644
index 92111b05b..000000000
--- a/test/integration/tests.js
+++ /dev/null
@@ -1,24 +0,0 @@
-QUnit.test('agree to terms', function (assert) {
- var done = assert.async()
-
- // Select the mock app root
- var app = $('iframe').contents().find('#app-content .mock-app-root')
-
- app.find('.markdown').prop('scrollTop', 100000000)
-
- wait().then(function() {
- app.find('button').click()
- }).then(function() {
- return wait()
- }).then(function() {
- var title = app.find('h1').text()
- assert.equal(title, 'MetaMask', 'title screen')
-
- var buttons = app.find('button')
- assert.equal(buttons.length, 2, 'two buttons: create and restore')
-
- done()
- })
-
- // Wait for view to transition:
-})
diff --git a/test/lib/mock-config-manager.js b/test/lib/mock-config-manager.js
index fe841f455..ccd518c68 100644
--- a/test/lib/mock-config-manager.js
+++ b/test/lib/mock-config-manager.js
@@ -1,5 +1,5 @@
var ConfigManager = require('../../app/scripts/lib/config-manager')
-const STORAGE_KEY = 'metamask-persistance-key'
+const STORAGE_KEY = 'metamask-config'
const extend = require('xtend')
module.exports = function() {
diff --git a/test/lib/mock-encryptor.js b/test/lib/mock-encryptor.js
new file mode 100644
index 000000000..09bbf7ad5
--- /dev/null
+++ b/test/lib/mock-encryptor.js
@@ -0,0 +1,32 @@
+var mockHex = '0xabcdef0123456789'
+var mockKey = new Buffer(32)
+let cacheVal
+
+module.exports = {
+
+ encrypt(password, dataObj) {
+ cacheVal = dataObj
+ return Promise.resolve(mockHex)
+ },
+
+ decrypt(password, text) {
+ return Promise.resolve(cacheVal || {})
+ },
+
+ encryptWithKey(key, dataObj) {
+ return this.encrypt(key, dataObj)
+ },
+
+ decryptWithKey(key, text) {
+ return this.decrypt(key, text)
+ },
+
+ keyFromPassword(password) {
+ return Promise.resolve(mockKey)
+ },
+
+ generateSalt() {
+ return 'WHADDASALT!'
+ },
+
+}
diff --git a/test/lib/mock-simple-keychain.js b/test/lib/mock-simple-keychain.js
new file mode 100644
index 000000000..615b3e537
--- /dev/null
+++ b/test/lib/mock-simple-keychain.js
@@ -0,0 +1,38 @@
+var fakeWallet = {
+ privKey: '0x123456788890abcdef',
+ address: '0xfedcba0987654321',
+}
+const type = 'Simple Key Pair'
+
+module.exports = class MockSimpleKeychain {
+
+ static type() { return type }
+
+ constructor(opts) {
+ this.type = type
+ this.opts = opts || {}
+ this.wallets = []
+ }
+
+ serialize() {
+ return [ fakeWallet.privKey ]
+ }
+
+ deserialize(data) {
+ if (!Array.isArray(data)) {
+ throw new Error('Simple keychain deserialize requires a privKey array.')
+ }
+ this.wallets = [ fakeWallet ]
+ }
+
+ addAccounts(n = 1) {
+ for(var i = 0; i < n; i++) {
+ this.wallets.push(fakeWallet)
+ }
+ }
+
+ getAccounts() {
+ return this.wallets.map(w => w.address)
+ }
+
+}
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..f72ca82e4 100644
--- a/test/unit/actions/set_selected_account_test.js
+++ b/test/unit/actions/set_selected_account_test.js
@@ -44,6 +44,5 @@ describe('SHOW_ACCOUNT_DETAIL', function() {
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..1f06b1120 100644
--- a/test/unit/actions/tx_test.js
+++ b/test/unit/actions/tx_test.js
@@ -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() },
})
@@ -135,7 +135,7 @@ describe('tx confirmation screen', function() {
}
freeze(initialState)
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb() },
})
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
index 26aa35a74..477188b67 100644
--- a/test/unit/config-manager-test.js
+++ b/test/unit/config-manager-test.js
@@ -100,31 +100,31 @@ describe('config-manager', function() {
describe('confirmation', function() {
- describe('#getConfirmed', function() {
+ describe('#getConfirmedDisclaimer', function() {
it('should return false if no previous key exists', function() {
- var result = configManager.getConfirmed()
+ var result = configManager.getConfirmedDisclaimer()
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()
+ describe('#setConfirmedDisclaimer', function() {
+ it('should make getConfirmedDisclaimer return true once set', function() {
+ assert.equal(configManager.getConfirmedDisclaimer(), false)
+ configManager.setConfirmedDisclaimer(true)
+ var result = configManager.getConfirmedDisclaimer()
assert.equal(result, true)
})
it('should be able to set false', function() {
- configManager.setConfirmed(false)
- var result = configManager.getConfirmed()
+ configManager.setConfirmedDisclaimer(false)
+ var result = configManager.getConfirmedDisclaimer()
assert.equal(result, false)
})
it('should persist to local storage', function() {
- configManager.setConfirmed(true)
+ configManager.setConfirmedDisclaimer(true)
var data = configManager.getData()
- assert.equal(data.isConfirmed, true)
+ assert.equal(data.isDisclaimerConfirmed, true)
})
})
})
@@ -153,7 +153,7 @@ describe('config-manager', function() {
rpcTarget: 'foobar'
},
}
- configManager.setConfirmed(true)
+ configManager.setConfirmedDisclaimer(true)
configManager.setConfig(testConfig)
var testWallet = {
@@ -164,7 +164,7 @@ 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)
+ assert.equal(configManager.getConfirmedDisclaimer(), true)
testConfig.provider.type = 'something else!'
configManager.setConfig(testConfig)
@@ -173,7 +173,7 @@ 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)
+ assert.equal(configManager.getConfirmedDisclaimer(), true)
})
})
diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js
new file mode 100644
index 000000000..ac8e23d22
--- /dev/null
+++ b/test/unit/idStore-migration-test.js
@@ -0,0 +1,160 @@
+const async = require('async')
+const assert = require('assert')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const ConfigManager = require('../../app/scripts/lib/config-manager')
+const delegateCallCode = require('../lib/example-code.json').delegateCallCode
+
+// The old way:
+const IdentityStore = require('../../app/scripts/lib/idStore')
+const STORAGE_KEY = 'metamask-config'
+const extend = require('xtend')
+
+// 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',
+}
+
+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()
+ window.localStorage = {} // Hacking localStorage support into JSDom
+ configManager = new ConfigManager({
+ loadData,
+ setData: (d) => { window.localStorage = d }
+ })
+
+
+ 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] },
+ },
+ })
+
+ // Stub out the browser crypto for a mock encryptor.
+ // Browser crypto is tested in the integration test suite.
+ keyringController.encryptor = mockEncryptor
+ done()
+ })
+ })
+ })
+
+ describe('entering a password', function() {
+ it('should identify an old wallet as an initialized keyring', function() {
+ keyringController.configManager.setWallet('something')
+ const state = keyringController.getState()
+ assert(state.isInitialized, 'old vault counted as initialized.')
+ })
+
+ /*
+ it('should use the password to migrate the old vault', function(done) {
+ this.timeout(5000)
+ console.log('calling submitPassword')
+ console.dir(keyringController)
+ keyringController.submitPassword(password, function (err, state) {
+ assert.ifError(err, 'submitPassword threw error')
+
+ function log(str, dat) { console.log(str + ': ' + JSON.stringify(dat)) }
+
+ let newAccounts = keyringController.getAccounts()
+ log('new accounts: ', newAccounts)
+
+ let newAccount = ethUtil.addHexPrefix(newAccounts[0])
+ assert.equal(ethUtil.addHexPrefix(newAccount), mockVault.account, 'restored the correct account')
+ const newSeed = keyringController.keyrings[0].mnemonic
+ log('keyringController keyrings', keyringController.keyrings)
+ assert.equal(newSeed, mockVault.seed, 'seed phrase transferred.')
+
+ assert(configManager.getVault(), 'new type of vault is persisted')
+ done()
+ })
+ })
+ */
+
+ })
+})
+
+function loadData () {
+ var oldData = getOldStyleData()
+ var newData
+ try {
+ newData = JSON.parse(window.localStorage[STORAGE_KEY])
+ } catch (e) {}
+
+ var data = extend({
+ meta: {
+ version: 0,
+ },
+ data: {
+ config: {
+ provider: {
+ type: 'testnet',
+ },
+ },
+ },
+ }, oldData || null, newData || null)
+ return data
+}
+
+function setData (data) {
+ window.localStorage[STORAGE_KEY] = JSON.stringify(data)
+}
+
+function getOldStyleData () {
+ var config, wallet, seedWords
+
+ var result = {
+ meta: { version: 0 },
+ data: {},
+ }
+
+ try {
+ config = JSON.parse(window.localStorage['config'])
+ result.data.config = config
+ } catch (e) {}
+ try {
+ wallet = JSON.parse(window.localStorage['lightwallet'])
+ result.data.wallet = wallet
+ } catch (e) {}
+ try {
+ seedWords = window.localStorage['seedWords']
+ result.data.seedWords = seedWords
+ } catch (e) {}
+
+ return result
+}
diff --git a/test/unit/idStore-test.js b/test/unit/idStore-test.js
index bf8270540..3ca89cd38 100644
--- a/test/unit/idStore-test.js
+++ b/test/unit/idStore-test.js
@@ -11,7 +11,6 @@ describe('IdentityStore', function() {
describe('#createNewVault', function () {
let idStore
let password = 'password123'
- let entropy = 'entripppppyy duuude'
let seedWords
let accounts = []
let originalKeystore
@@ -26,7 +25,7 @@ describe('IdentityStore', function() {
},
})
- idStore.createNewVault(password, entropy, (err, seeds) => {
+ idStore.createNewVault(password, (err, seeds) => {
assert.ifError(err, 'createNewVault threw error')
seedWords = seeds
originalKeystore = idStore._idmgmt.keyStore
diff --git a/test/unit/keyring-controller-test.js b/test/unit/keyring-controller-test.js
new file mode 100644
index 000000000..69a57ef52
--- /dev/null
+++ b/test/unit/keyring-controller-test.js
@@ -0,0 +1,200 @@
+var assert = require('assert')
+var KeyringController = require('../../app/scripts/keyring-controller')
+var 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(),
+ 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()
+ })
+ })
+
+ afterEach(function() {
+ // Cleanup mocks
+ this.sinon.restore()
+ })
+
+ describe('#createNewVaultAndKeychain', function () {
+ this.timeout(10000)
+
+ it('should set a vault on the configManager', function(done) {
+ keyringController.configManager.setVault(null)
+ assert(!keyringController.configManager.getVault(), 'no previous vault')
+ keyringController.createNewVaultAndKeychain(password)
+ .then(() => {
+ const vault = keyringController.configManager.getVault()
+ assert(vault, 'vault created')
+ done()
+ })
+ .catch((reason) => {
+ assert.ifError(reason)
+ done()
+ })
+ })
+ })
+
+ 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) => {
+ assert.ifError(reason)
+ done()
+ })
+ })
+ })
+
+ describe('#migrateOldVaultIfAny', function() {
+ it('should return and init a new vault', function(done) {
+ keyringController.migrateOldVaultIfAny(password)
+ .then(() => {
+ assert(keyringController.configManager.getVault(), 'now has a vault')
+ assert(keyringController.password, 'has a password set')
+ done()
+ })
+ .catch((reason) => {
+ assert.ifError(reason)
+ done()
+ })
+ })
+ })
+
+ describe('#createNickname', function() {
+ it('should add the address to the identities hash', function() {
+ const fakeAddress = '0x12345678'
+ keyringController.createNickname(fakeAddress)
+ const identities = keyringController.identities
+ const identity = identities[fakeAddress]
+ assert.equal(identity.address, fakeAddress)
+
+ const nick = keyringController.configManager.nicknameForWallet(fakeAddress)
+ assert.equal(typeof nick, 'string')
+ })
+ })
+
+ describe('#saveAccountLabel', function() {
+ it ('sets the nickname', function(done) {
+ const account = addresses[0]
+ var nick = 'Test nickname'
+ keyringController.identities[ethUtil.addHexPrefix(account)] = {}
+ keyringController.saveAccountLabel(account, nick)
+ .then((label) => {
+ assert.equal(label, nick)
+ const persisted = keyringController.configManager.nicknameForWallet(account)
+ assert.equal(persisted, nick)
+ done()
+ })
+ .catch((reason) => {
+ assert.ifError(reason)
+ done()
+ })
+ })
+
+ this.timeout(10000)
+ it('retrieves the persisted nickname', function(done) {
+ const account = addresses[0]
+ var nick = 'Test nickname'
+ keyringController.configManager.setNicknameForWallet(account, nick)
+ keyringController.createNewVaultAndRestore(password, seedWords)
+ .then((state) => {
+
+ const identity = keyringController.identities['0x' + account]
+ assert.equal(identity.name, nick)
+
+ assert(accounts)
+ done()
+ })
+ .catch((reason) => {
+ assert.ifError(reason)
+ done()
+ })
+ })
+ })
+
+ 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..979abdb69
--- /dev/null
+++ b/test/unit/keyrings/simple-test.js
@@ -0,0 +1,94 @@
+const assert = require('assert')
+const extend = require('xtend')
+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('#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 = 'foo'
+ keyring.wallets.push({
+ getAddress() {
+ return {
+ toString() {
+ return desiredOutput
+ }
+ }
+ }
+ })
+
+ keyring.getAccounts()
+ .then((output) => {
+ assert.equal(output[0], desiredOutput)
+ assert.equal(output.length, 1)
+ 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()
+ })
+ })
+
+})