aboutsummaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/account-link-test.js18
-rw-r--r--test/unit/actions/config_test.js23
-rw-r--r--test/unit/actions/save_account_label_test.js21
-rw-r--r--test/unit/actions/set_selected_account_test.js23
-rw-r--r--test/unit/actions/tx_test.js142
-rw-r--r--test/unit/actions/view_info_test.js15
-rw-r--r--test/unit/actions/warning_test.js13
-rw-r--r--test/unit/address-book-controller.js16
-rw-r--r--test/unit/blacklist-controller-test.js41
-rw-r--r--test/unit/components/balance-component-test.js45
-rw-r--r--test/unit/components/binary-renderer-test.js10
-rw-r--r--test/unit/components/bn-as-decimal-input-test.js89
-rw-r--r--test/unit/components/pending-tx-test.js67
-rw-r--r--test/unit/config-manager-test.js42
-rw-r--r--test/unit/currency-controller-test.js59
-rw-r--r--test/unit/explorer-link-test.js16
-rw-r--r--test/unit/infura-controller-test.js62
-rw-r--r--test/unit/keyring-controller-test.js172
-rw-r--r--test/unit/linting_test.js9
-rw-r--r--test/unit/message-manager-test.js41
-rw-r--r--test/unit/metamask-controller-test.js114
-rw-r--r--test/unit/migrations-test.js10
-rw-r--r--test/unit/migrations/021-test.js16
-rw-r--r--test/unit/migrator-test.js41
-rw-r--r--test/unit/nameForAccount_test.js17
-rw-r--r--test/unit/network-contoller-test.js84
-rw-r--r--test/unit/nodeify-test.js18
-rw-r--r--test/unit/nonce-tracker-test.js203
-rw-r--r--test/unit/notice-controller-test.js71
-rw-r--r--test/unit/pending-balance-test.js93
-rw-r--r--test/unit/pending-tx-test.js402
-rw-r--r--test/unit/personal-message-manager-test.js47
-rw-r--r--test/unit/preferences-controller-test.js48
-rw-r--r--test/unit/reducers/unlock_vault_test.js27
-rw-r--r--test/unit/responsive/components/dropdown-test.js81
-rw-r--r--test/unit/tx-controller-test.js413
-rw-r--r--test/unit/tx-gas-util-test.js32
-rw-r--r--test/unit/tx-helper-test.js17
-rw-r--r--test/unit/tx-manager-test.js241
-rw-r--r--test/unit/tx-state-history-helper-test.js26
-rw-r--r--test/unit/tx-state-history-helper.js46
-rw-r--r--test/unit/tx-state-manager-test.js284
-rw-r--r--test/unit/tx-utils-test.js50
-rw-r--r--test/unit/ui/add-token.spec.js43
-rw-r--r--test/unit/util-test.js41
-rw-r--r--test/unit/util_test.js131
46 files changed, 2613 insertions, 907 deletions
diff --git a/test/unit/account-link-test.js b/test/unit/account-link-test.js
deleted file mode 100644
index 803a70f37..000000000
--- a/test/unit/account-link-test.js
+++ /dev/null
@@ -1,18 +0,0 @@
-var assert = require('assert')
-var linkGen = require('../../ui/lib/account-link')
-
-describe('account-link', function() {
-
- it('adds ropsten prefix to ropsten test network', function() {
- var result = linkGen('account', '3')
- assert.notEqual(result.indexOf('ropsten'), -1, 'ropsten included')
- assert.notEqual(result.indexOf('account'), -1, 'account included')
- })
-
- it('adds kovan prefix to kovan test network', function() {
- var result = linkGen('account', '42')
- assert.notEqual(result.indexOf('kovan'), -1, 'kovan included')
- assert.notEqual(result.indexOf('account'), -1, 'account included')
- })
-
-})
diff --git a/test/unit/actions/config_test.js b/test/unit/actions/config_test.js
index 14198fa8a..648f456fb 100644
--- a/test/unit/actions/config_test.js
+++ b/test/unit/actions/config_test.js
@@ -1,36 +1,34 @@
-var jsdom = require('mocha-jsdom')
+// var jsdom = require('mocha-jsdom')
var assert = require('assert')
var freeze = require('deep-freeze-strict')
var path = require('path')
var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
-var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-
-describe ('config view actions', function() {
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
+describe('config view actions', function () {
var initialState = {
metamask: {
rpcTarget: 'foo',
- frequentRpcList: []
+ frequentRpcList: [],
},
appState: {
currentView: {
name: 'accounts',
- }
- }
+ },
+ },
}
freeze(initialState)
- describe('SHOW_CONFIG_PAGE', function() {
- it('should set appState.currentView.name to config', function() {
+ describe('SHOW_CONFIG_PAGE', function () {
+ it('should set appState.currentView.name to config', function () {
var result = reducers(initialState, actions.showConfigPage())
assert.equal(result.appState.currentView.name, 'config')
})
})
- describe('SET_RPC_TARGET', function() {
-
- it('sets the state.metamask.rpcTarget property of the state to the action.value', function() {
+ describe('SET_RPC_TARGET', function () {
+ it('sets the state.metamask.rpcTarget property of the state to the action.value', function () {
const action = {
type: actions.SET_RPC_TARGET,
value: 'foo',
@@ -41,5 +39,4 @@ describe ('config view actions', function() {
assert.equal(result.metamask.provider.rpcTarget, 'foo')
})
})
-
})
diff --git a/test/unit/actions/save_account_label_test.js b/test/unit/actions/save_account_label_test.js
index 1df428b1d..c5ffd6cbf 100644
--- a/test/unit/actions/save_account_label_test.js
+++ b/test/unit/actions/save_account_label_test.js
@@ -1,22 +1,21 @@
-var jsdom = require('mocha-jsdom')
+// var jsdom = require('mocha-jsdom')
var assert = require('assert')
var freeze = require('deep-freeze-strict')
var path = require('path')
var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
-var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-describe('SAVE_ACCOUNT_LABEL', function() {
-
- it('updates the state.metamask.identities[:i].name property of the state to the action.value.label', function() {
+describe('SAVE_ACCOUNT_LABEL', function () {
+ it('updates the state.metamask.identities[:i].name property of the state to the action.value.label', function () {
var initialState = {
metamask: {
identities: {
foo: {
- name: 'bar'
- }
+ name: 'bar',
+ },
},
- }
+ },
}
freeze(initialState)
@@ -24,13 +23,13 @@ describe('SAVE_ACCOUNT_LABEL', function() {
type: actions.SAVE_ACCOUNT_LABEL,
value: {
account: 'foo',
- label: 'baz'
+ label: 'baz',
},
}
freeze(action)
var resultingState = reducers(initialState, action)
assert.equal(resultingState.metamask.identities.foo.name, action.value.label)
- });
-});
+ })
+})
diff --git a/test/unit/actions/set_selected_account_test.js b/test/unit/actions/set_selected_account_test.js
index 2dc42d2ec..28b47d09d 100644
--- a/test/unit/actions/set_selected_account_test.js
+++ b/test/unit/actions/set_selected_account_test.js
@@ -1,18 +1,17 @@
-var jsdom = require('mocha-jsdom')
+// var jsdom = require('mocha-jsdom')
var assert = require('assert')
var freeze = require('deep-freeze-strict')
var path = require('path')
var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
-var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-describe('SET_SELECTED_ACCOUNT', function() {
-
- it('sets the state.appState.activeAddress property of the state to the action.value', function() {
+describe('SET_SELECTED_ACCOUNT', function () {
+ it('sets the state.appState.activeAddress property of the state to the action.value', function () {
var initialState = {
appState: {
activeAddress: 'foo',
- }
+ },
}
freeze(initialState)
@@ -24,15 +23,15 @@ describe('SET_SELECTED_ACCOUNT', function() {
var resultingState = reducers(initialState, action)
assert.equal(resultingState.appState.activeAddress, action.value)
- });
-});
+ })
+})
-describe('SHOW_ACCOUNT_DETAIL', function() {
- it('updates metamask state', function() {
+describe('SHOW_ACCOUNT_DETAIL', function () {
+ it('updates metamask state', function () {
var initialState = {
metamask: {
- selectedAddress: 'foo'
- }
+ selectedAddress: 'foo',
+ },
}
freeze(initialState)
diff --git a/test/unit/actions/tx_test.js b/test/unit/actions/tx_test.js
index bd72a666e..b6a691860 100644
--- a/test/unit/actions/tx_test.js
+++ b/test/unit/actions/tx_test.js
@@ -1,29 +1,27 @@
-var jsdom = require('mocha-jsdom')
+// 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'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-describe('tx confirmation screen', function() {
-
- beforeEach(function() {
- this.sinon = sinon.sandbox.create();
- });
+describe('tx confirmation screen', function () {
+ beforeEach(function () {
+ this.sinon = sinon.sandbox.create()
+ })
- afterEach(function(){
- this.sinon.restore();
- });
+ afterEach(function () {
+ this.sinon.restore()
+ })
var initialState, result
- describe('when there is only one tx', function() {
+ describe('when there is only one tx', function () {
var firstTxId = 1457634084250832
- beforeEach(function() {
-
+ beforeEach(function () {
initialState = {
appState: {
currentView: {
@@ -34,130 +32,42 @@ describe('tx confirmation screen', function() {
unapprovedTxs: {
'1457634084250832': {
id: 1457634084250832,
- status: "unconfirmed",
+ status: 'unconfirmed',
time: 1457634084250,
- }
+ },
},
- }
+ },
}
freeze(initialState)
})
- describe('cancelTx', function() {
-
- before(function(done) {
+ describe('cancelTx', function () {
+ before(function (done) {
actions._setBackgroundConnection({
- approveTransaction(txId, cb) { cb('An error!') },
- cancelTransaction(txId) { /* noop */ },
- clearSeedWordCache(cb) { cb() },
+ approveTransaction (txId, cb) { cb('An error!') },
+ cancelTransaction (txId, cb) { cb() },
+ clearSeedWordCache (cb) { cb() },
})
- let action = actions.cancelTx({value: firstTxId})
- result = reducers(initialState, action)
+ actions.cancelTx({value: firstTxId})((action) => {
+ result = reducers(initialState, action)
+ })
done()
})
- it('should transition to the account detail view', function() {
+ it('should transition to the account detail view', function () {
assert.equal(result.appState.currentView.name, 'accountDetail')
})
- it('should have no unconfirmed txs remaining', function() {
+ it('should have no unconfirmed txs remaining', function () {
var count = getUnconfirmedTxCount(result)
assert.equal(count, 0)
})
})
-
- describe('sendTx', function() {
- var result
-
- describe('when there is an error', function() {
-
- before(function(done) {
- alert = () => {/* noop */}
-
- actions._setBackgroundConnection({
- approveTransaction(txId, cb) { cb({message: 'An error!'}) },
- })
-
- actions.sendTx({id: firstTxId})(function(action) {
- result = reducers(initialState, action)
- done()
- })
- })
-
- it('should stay on the page', function() {
- assert.equal(result.appState.currentView.name, 'confTx')
- })
-
- it('should set errorMessage on the currentView', function() {
- assert(result.appState.currentView.errorMessage)
- })
- })
-
- describe('when there is success', function() {
- it('should complete tx and go home', function() {
- actions._setBackgroundConnection({
- approveTransaction(txId, cb) { cb() },
- })
-
- var dispatchExpect = sinon.mock()
- dispatchExpect.twice()
-
- actions.sendTx({id: firstTxId})(dispatchExpect)
- })
- })
- })
-
- describe('when there are two pending txs', function() {
- var firstTxId = 1457634084250832
- var result, initialState
- before(function(done) {
- initialState = {
- appState: {
- currentView: {
- name: 'confTx',
- },
- },
- metamask: {
- unapprovedTxs: {
- '1457634084250832': {
- id: firstTxId,
- status: "unconfirmed",
- time: 1457634084250,
- },
- '1457634084250833': {
- id: 1457634084250833,
- status: "unconfirmed",
- time: 1457634084255,
- },
- },
- }
- }
- freeze(initialState)
-
- // Mocking a background connection:
- actions._setBackgroundConnection({
- approveTransaction(firstTxId, cb) { cb() },
- })
-
- let action = actions.sendTx({id: firstTxId})(function(action) {
- result = reducers(initialState, action)
- })
- done()
- })
-
- it('should stay on the confTx view', function() {
- assert.equal(result.appState.currentView.name, 'confTx')
- })
-
- it('should transition to the first tx', function() {
- assert.equal(result.appState.currentView.context, 0)
- })
- })
})
-});
+})
-function getUnconfirmedTxCount(state) {
+function getUnconfirmedTxCount (state) {
var txs = state.metamask.unapprovedTxs
var count = Object.keys(txs).length
return count
diff --git a/test/unit/actions/view_info_test.js b/test/unit/actions/view_info_test.js
index 0558c6e42..69895d801 100644
--- a/test/unit/actions/view_info_test.js
+++ b/test/unit/actions/view_info_test.js
@@ -1,23 +1,22 @@
-var jsdom = require('mocha-jsdom')
+// var jsdom = require('mocha-jsdom')
var assert = require('assert')
var freeze = require('deep-freeze-strict')
var path = require('path')
var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
-var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-describe('SHOW_INFO_PAGE', function() {
-
- it('sets the state.appState.currentView.name property to info', function() {
+describe('SHOW_INFO_PAGE', function () {
+ it('sets the state.appState.currentView.name property to info', function () {
var initialState = {
appState: {
activeAddress: 'foo',
- }
+ },
}
freeze(initialState)
const action = actions.showInfoPage()
var resultingState = reducers(initialState, action)
assert.equal(resultingState.appState.currentView.name, 'info')
- });
-});
+ })
+})
diff --git a/test/unit/actions/warning_test.js b/test/unit/actions/warning_test.js
index 37be9ee85..28b565499 100644
--- a/test/unit/actions/warning_test.js
+++ b/test/unit/actions/warning_test.js
@@ -1,14 +1,13 @@
-var jsdom = require('mocha-jsdom')
+// var jsdom = require('mocha-jsdom')
var assert = require('assert')
var freeze = require('deep-freeze-strict')
var path = require('path')
var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
-var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-describe('action DISPLAY_WARNING', function() {
-
- it('sets appState.warning to provided value', function() {
+describe('action DISPLAY_WARNING', function () {
+ it('sets appState.warning to provided value', function () {
var initialState = {
appState: {},
}
@@ -20,5 +19,5 @@ describe('action DISPLAY_WARNING', function() {
const resultingState = reducers(initialState, action)
assert.equal(resultingState.appState.warning, warningText, 'warning text set')
- });
-});
+ })
+})
diff --git a/test/unit/address-book-controller.js b/test/unit/address-book-controller.js
index f345b0328..655c9022c 100644
--- a/test/unit/address-book-controller.js
+++ b/test/unit/address-book-controller.js
@@ -1,5 +1,4 @@
const assert = require('assert')
-const extend = require('xtend')
const AddressBookController = require('../../app/scripts/controllers/address-book')
const mockKeyringController = {
@@ -7,21 +6,20 @@ const mockKeyringController = {
getState: function () {
return {
identities: {
- '0x0aaa' : {
+ '0x0aaa': {
address: '0x0aaa',
name: 'owned',
- }
- }
+ },
+ },
}
- }
- }
+ },
+ },
}
-
-describe('address-book-controller', function() {
+describe('address-book-controller', function () {
var addressBookController
- beforeEach(function() {
+ beforeEach(function () {
addressBookController = new AddressBookController({}, mockKeyringController)
})
diff --git a/test/unit/blacklist-controller-test.js b/test/unit/blacklist-controller-test.js
new file mode 100644
index 000000000..a9260466f
--- /dev/null
+++ b/test/unit/blacklist-controller-test.js
@@ -0,0 +1,41 @@
+const assert = require('assert')
+const BlacklistController = require('../../app/scripts/controllers/blacklist')
+
+describe('blacklist controller', function () {
+ let blacklistController
+
+ before(() => {
+ blacklistController = new BlacklistController()
+ })
+
+ describe('checkForPhishing', function () {
+ it('should not flag whitelisted values', function () {
+ const result = blacklistController.checkForPhishing('www.metamask.io')
+ assert.equal(result, false)
+ })
+ it('should flag explicit values', function () {
+ const result = blacklistController.checkForPhishing('metamask.com')
+ assert.equal(result, true)
+ })
+ it('should flag levenshtein values', function () {
+ const result = blacklistController.checkForPhishing('metmask.io')
+ assert.equal(result, true)
+ })
+ it('should not flag not-even-close values', function () {
+ const result = blacklistController.checkForPhishing('example.com')
+ assert.equal(result, false)
+ })
+ it('should not flag the ropsten faucet domains', function () {
+ const result = blacklistController.checkForPhishing('faucet.metamask.io')
+ assert.equal(result, false)
+ })
+ it('should not flag the mascara domain', function () {
+ const result = blacklistController.checkForPhishing('zero.metamask.io')
+ assert.equal(result, false)
+ })
+ it('should not flag the mascara-faucet domain', function () {
+ const result = blacklistController.checkForPhishing('zero-faucet.metamask.io')
+ assert.equal(result, false)
+ })
+ })
+}) \ No newline at end of file
diff --git a/test/unit/components/balance-component-test.js b/test/unit/components/balance-component-test.js
new file mode 100644
index 000000000..9b1e82acf
--- /dev/null
+++ b/test/unit/components/balance-component-test.js
@@ -0,0 +1,45 @@
+const assert = require('assert')
+const h = require('react-hyperscript')
+const { createMockStore } = require('redux-test-utils')
+const { shallowWithStore } = require('../../lib/shallow-with-store')
+const BalanceComponent = require('../../../ui/app/components/balance-component')
+const mockState = {
+ metamask: {
+ accounts: { abc: {} },
+ network: 1,
+ selectedAddress: 'abc',
+ }
+}
+
+describe('BalanceComponent', function () {
+ let balanceComponent
+ let store
+ let component
+ beforeEach(function () {
+ store = createMockStore(mockState)
+ component = shallowWithStore(h(BalanceComponent), store)
+ balanceComponent = component.dive()
+ })
+
+ it('shows token balance and convert to fiat value based on conversion rate', function () {
+ const formattedBalance = '1.23 ETH'
+
+ const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
+ const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 2)
+
+ assert.equal('1.23 ETH', tokenBalance)
+ assert.equal(2.46, fiatDisplayNumber)
+ })
+
+ it('shows only the token balance when conversion rate is not available', function () {
+ const formattedBalance = '1.23 ETH'
+
+ const tokenBalance = balanceComponent.instance().getTokenBalance(formattedBalance, false)
+ const fiatDisplayNumber = balanceComponent.instance().getFiatDisplayNumber(formattedBalance, 0)
+
+ assert.equal('1.23 ETH', tokenBalance)
+ assert.equal('N/A', fiatDisplayNumber)
+ })
+
+})
+
diff --git a/test/unit/components/binary-renderer-test.js b/test/unit/components/binary-renderer-test.js
index 3264faddc..ee2fa8b60 100644
--- a/test/unit/components/binary-renderer-test.js
+++ b/test/unit/components/binary-renderer-test.js
@@ -1,24 +1,22 @@
var assert = require('assert')
var BinaryRenderer = require('../../../ui/app/components/binary-renderer')
-describe('BinaryRenderer', function() {
-
+describe('BinaryRenderer', function () {
let binaryRenderer
const message = 'Hello, world!'
const buffer = new Buffer(message, 'utf8')
const hex = buffer.toString('hex')
- beforeEach(function() {
+ beforeEach(function () {
binaryRenderer = new BinaryRenderer()
})
- it('recovers message', function() {
+ it('recovers message', function () {
const result = binaryRenderer.hexToText(hex)
assert.equal(result, message)
})
-
- it('recovers message with hex prefix', function() {
+ it('recovers message with hex prefix', function () {
const result = binaryRenderer.hexToText('0x' + hex)
assert.equal(result, message)
})
diff --git a/test/unit/components/bn-as-decimal-input-test.js b/test/unit/components/bn-as-decimal-input-test.js
new file mode 100644
index 000000000..58ecc9c89
--- /dev/null
+++ b/test/unit/components/bn-as-decimal-input-test.js
@@ -0,0 +1,89 @@
+var assert = require('assert')
+
+const additions = require('react-testutils-additions')
+const h = require('react-hyperscript')
+const ReactTestUtils = require('react-addons-test-utils')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+
+var BnInput = require('../../../ui/app/components/bn-as-decimal-input')
+
+describe('BnInput', function () {
+ it('can tolerate a gas decimal number at a high precision', function (done) {
+ const renderer = ReactTestUtils.createRenderer()
+
+ let valueStr = '20'
+ while (valueStr.length < 20) {
+ valueStr += '0'
+ }
+ const value = new BN(valueStr, 10)
+
+ const inputStr = '2.3'
+
+ let targetStr = '23'
+ while (targetStr.length < 19) {
+ targetStr += '0'
+ }
+ const target = new BN(targetStr, 10)
+
+ const precision = 18 // ether precision
+ const scale = 18
+
+ const props = {
+ value,
+ scale,
+ precision,
+ onChange: (newBn) => {
+ assert.equal(newBn.toString(), target.toString(), 'should tolerate increase')
+ done()
+ },
+ }
+
+ const inputComponent = h(BnInput, props)
+ const component = additions.renderIntoDocument(inputComponent)
+ renderer.render(inputComponent)
+ const input = additions.find(component, 'input.hex-input')[0]
+ ReactTestUtils.Simulate.change(input, { preventDefault () {}, target: {
+ value: inputStr,
+ checkValidity () { return true } },
+ })
+ })
+
+ it('can tolerate wei precision', function (done) {
+ const renderer = ReactTestUtils.createRenderer()
+
+ let valueStr = '1000000000'
+
+ const value = new BN(valueStr, 10)
+ const inputStr = '1.000000001'
+
+
+ let targetStr = '1000000001'
+
+ const target = new BN(targetStr, 10)
+
+ const precision = 9 // gwei precision
+ const scale = 9
+
+ const props = {
+ value,
+ scale,
+ precision,
+ onChange: (newBn) => {
+ assert.equal(newBn.toString(), target.toString(), 'should tolerate increase')
+ const reInput = BnInput.prototype.downsize(newBn.toString(), 9, 9)
+ assert.equal(reInput.toString(), inputStr, 'should tolerate increase')
+ done()
+ },
+ }
+
+ const inputComponent = h(BnInput, props)
+ const component = additions.renderIntoDocument(inputComponent)
+ renderer.render(inputComponent)
+ const input = additions.find(component, 'input.hex-input')[0]
+ ReactTestUtils.Simulate.change(input, { preventDefault () {}, target: {
+ value: inputStr,
+ checkValidity () { return true } },
+ })
+ })
+})
diff --git a/test/unit/components/pending-tx-test.js b/test/unit/components/pending-tx-test.js
new file mode 100644
index 000000000..c6c588e1c
--- /dev/null
+++ b/test/unit/components/pending-tx-test.js
@@ -0,0 +1,67 @@
+const assert = require('assert')
+const h = require('react-hyperscript')
+const PendingTx = require('../../../ui/app/components/pending-tx')
+const ethUtil = require('ethereumjs-util')
+
+const { createMockStore } = require('redux-test-utils')
+const { shallowWithStore } = require('../../lib/shallow-with-store')
+
+const identities = { abc: {}, def: {} }
+const mockState = {
+ metamask: {
+ accounts: { abc: {} },
+ identities,
+ conversionRate: 10,
+ selectedAddress: 'abc',
+ }
+}
+
+describe('PendingTx', function () {
+ const gasPrice = '0x4A817C800' // 20 Gwei
+ const txData = {
+ 'id': 5021615666270214,
+ 'time': 1494458763011,
+ 'status': 'unapproved',
+ 'metamaskNetworkId': '1494442339676',
+ 'txParams': {
+ 'from': '0xfdea65c8e26263f6d9a1b5de9555d2931a33b826',
+ 'to': '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
+ 'value': '0xde0b6b3a7640000',
+ gasPrice,
+ 'gas': '0x7b0c',
+ },
+ 'gasLimitSpecified': false,
+ 'estimatedGas': '0x5208',
+ }
+ const newGasPrice = '0x77359400'
+
+ const computedBalances = {}
+ computedBalances[Object.keys(identities)[0]] = {
+ ethBalance: '0x00000000000000056bc75e2d63100000',
+ }
+ const props = {
+ txData,
+ computedBalances,
+ sendTransaction: (txMeta, event) => {
+ // Assert changes:
+ const result = ethUtil.addHexPrefix(txMeta.txParams.gasPrice)
+ assert.notEqual(result, gasPrice, 'gas price should change')
+ assert.equal(result, newGasPrice, 'gas price assigned.')
+ },
+ }
+
+ let pendingTxComponent
+ let store
+ let component
+ beforeEach(function () {
+ store = createMockStore(mockState)
+ component = shallowWithStore(h(PendingTx, props), store)
+ pendingTxComponent = component
+ })
+
+ it('should render correctly', function (done) {
+ assert.equal(pendingTxComponent.props().identities, identities)
+ done()
+ })
+})
+
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
index 05324e741..b710e2dfb 100644
--- a/test/unit/config-manager-test.js
+++ b/test/unit/config-manager-test.js
@@ -2,26 +2,22 @@
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 configManagerGen = require('../lib/mock-config-manager')
-describe('config-manager', function() {
+describe('config-manager', function () {
var configManager
- beforeEach(function() {
+ beforeEach(function () {
configManager = configManagerGen()
})
- describe('#setConfig', function() {
-
+ describe('#setConfig', function () {
it('should set the config key', function () {
var testConfig = {
provider: {
type: 'rpc',
- rpcTarget: 'foobar'
- }
+ rpcTarget: 'foobar',
+ },
}
configManager.setConfig(testConfig)
var result = configManager.getData()
@@ -30,17 +26,17 @@ describe('config-manager', function() {
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
})
- it('setting wallet should not overwrite config', function() {
+ it('setting wallet should not overwrite config', function () {
var testConfig = {
provider: {
type: 'rpc',
- rpcTarget: 'foobar'
+ rpcTarget: 'foobar',
},
}
configManager.setConfig(testConfig)
var testWallet = {
- name: 'this is my fake wallet'
+ name: 'this is my fake wallet',
}
configManager.setWallet(testWallet)
@@ -58,13 +54,13 @@ describe('config-manager', function() {
})
})
- describe('wallet nicknames', function() {
- it('should return null when no nicknames are saved', function() {
+ describe('wallet nicknames', function () {
+ it('should return null when no nicknames are saved', function () {
var nick = configManager.nicknameForWallet('0x0')
assert.equal(nick, null, 'no nickname returned')
})
- it('should persist nicknames', function() {
+ it('should persist nicknames', function () {
var account = '0x0'
var nick1 = 'foo'
var nick2 = 'bar'
@@ -79,8 +75,8 @@ describe('config-manager', function() {
})
})
- describe('rpc manipulations', function() {
- it('changing rpc should return a different rpc', function() {
+ describe('rpc manipulations', function () {
+ it('changing rpc should return a different rpc', function () {
var firstRpc = 'first'
var secondRpc = 'second'
@@ -94,21 +90,21 @@ describe('config-manager', function() {
})
})
- describe('transactions', function() {
- beforeEach(function() {
+ describe('transactions', function () {
+ beforeEach(function () {
configManager.setTxList([])
})
- describe('#getTxList', function() {
- it('when new should return empty array', function() {
+ describe('#getTxList', function () {
+ it('when new should return empty array', function () {
var result = configManager.getTxList()
assert.ok(Array.isArray(result))
assert.equal(result.length, 0)
})
})
- describe('#setTxList', function() {
- it('saves the submitted data to the tx list', function() {
+ describe('#setTxList', function () {
+ it('saves the submitted data to the tx list', function () {
var target = [{ foo: 'bar' }]
configManager.setTxList(target)
var result = configManager.getTxList()
diff --git a/test/unit/currency-controller-test.js b/test/unit/currency-controller-test.js
index 079f8b488..63ab60f9e 100644
--- a/test/unit/currency-controller-test.js
+++ b/test/unit/currency-controller-test.js
@@ -2,86 +2,81 @@
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/controllers/currency')
-describe('currency-controller', function() {
+describe('currency-controller', function () {
var currencyController
- beforeEach(function() {
+ beforeEach(function () {
currencyController = new CurrencyController()
})
- describe('currency conversions', function() {
-
- describe('#setCurrentCurrency', function() {
- it('should return USD as default', function() {
- assert.equal(currencyController.getCurrentCurrency(), 'USD')
+ 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')
+ 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() {
+ 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) {
+ 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":""}')
+ nock('https://api.infura.io')
+ .get('/v1/ticker/ethusd')
+ .reply(200, '{"base": "ETH", "quote": "USD", "bid": 288.45, "ask": 288.46, "volume": 112888.17569277, "exchange": "bitfinex", "total_volume": 272175.00106721005, "num_exchanges": 8, "timestamp": 1506444677}')
assert.equal(currencyController.getConversionRate(), 0)
- currencyController.setCurrentCurrency('USD')
+ currencyController.setCurrentCurrency('usd')
currencyController.updateConversionRate()
- .then(function() {
+ .then(function () {
var result = currencyController.getConversionRate()
console.log('currencyController.getConversionRate:', result)
assert.equal(typeof result, 'number')
done()
- }).catch(function(err) {
+ }).catch(function (err) {
done(err)
})
-
})
- it('should work for JPY as well.', function() {
+ 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":""}')
+ nock('https://api.infura.io')
+ .get('/v1/ticker/ethjpy')
+ .reply(200, '{"base": "ETH", "quote": "JPY", "bid": 32300.0, "ask": 32400.0, "volume": 247.4616071, "exchange": "kraken", "total_volume": 247.4616071, "num_exchanges": 1, "timestamp": 1506444676}')
var promise = new Promise(
function (resolve, reject) {
- currencyController.setCurrentCurrency('JPY')
- currencyController.updateConversionRate().then(function() {
+ currencyController.setCurrentCurrency('jpy')
+ currencyController.updateConversionRate().then(function () {
resolve()
})
- })
+ })
- promise.then(function() {
+ promise.then(function () {
var result = currencyController.getConversionRate()
assert.equal(typeof result, 'number')
- }).catch(function(err) {
+ }).catch(function (done, err) {
done(err)
})
})
})
})
-
})
diff --git a/test/unit/explorer-link-test.js b/test/unit/explorer-link-test.js
deleted file mode 100644
index 4f0230c2c..000000000
--- a/test/unit/explorer-link-test.js
+++ /dev/null
@@ -1,16 +0,0 @@
-var assert = require('assert')
-var linkGen = require('../../ui/lib/explorer-link')
-
-describe('explorer-link', function() {
-
- it('adds ropsten prefix to ropsten test network', function() {
- var result = linkGen('hash', '3')
- assert.notEqual(result.indexOf('ropsten'), -1, 'ropsten injected')
- })
-
- it('adds kovan prefix to kovan test network', function() {
- var result = linkGen('hash', '42')
- assert.notEqual(result.indexOf('kovan'), -1, 'kovan injected')
- })
-
-})
diff --git a/test/unit/infura-controller-test.js b/test/unit/infura-controller-test.js
new file mode 100644
index 000000000..605305efa
--- /dev/null
+++ b/test/unit/infura-controller-test.js
@@ -0,0 +1,62 @@
+const assert = require('assert')
+const sinon = require('sinon')
+const InfuraController = require('../../app/scripts/controllers/infura')
+
+describe('infura-controller', function () {
+ let infuraController, sandbox, networkStatus
+ const response = {'mainnet': 'degraded', 'ropsten': 'ok', 'kovan': 'ok', 'rinkeby': 'down'}
+
+ before(async function () {
+ infuraController = new InfuraController()
+ sandbox = sinon.sandbox.create()
+ sinon.stub(infuraController, 'checkInfuraNetworkStatus').resolves(response)
+ networkStatus = await infuraController.checkInfuraNetworkStatus()
+ })
+
+ after(function () {
+ sandbox.restore()
+ })
+
+ describe('Network status queries', function () {
+
+ describe('Mainnet', function () {
+ it('should have Mainnet', function () {
+ assert.equal(Object.keys(networkStatus)[0], 'mainnet')
+ })
+
+ it('should have a value for Mainnet status', function () {
+ assert.equal(networkStatus.mainnet, 'degraded')
+ })
+ })
+
+ describe('Ropsten', function () {
+ it('should have Ropsten', function () {
+ assert.equal(Object.keys(networkStatus)[1], 'ropsten')
+ })
+
+ it('should have a value for Ropsten status', function () {
+ assert.equal(networkStatus.ropsten, 'ok')
+ })
+ })
+
+ describe('Kovan', function () {
+ it('should have Kovan', function () {
+ assert.equal(Object.keys(networkStatus)[2], 'kovan')
+ })
+
+ it('should have a value for Kovan status', function () {
+ assert.equal(networkStatus.kovan, 'ok')
+ })
+ })
+
+ describe('Rinkeby', function () {
+ it('should have Rinkeby', function () {
+ assert.equal(Object.keys(networkStatus)[3], 'rinkeby')
+ })
+
+ it('should have a value for Rinkeby status', function () {
+ assert.equal(networkStatus.rinkeby, 'down')
+ })
+ })
+ })
+})
diff --git a/test/unit/keyring-controller-test.js b/test/unit/keyring-controller-test.js
deleted file mode 100644
index efd0a3546..000000000
--- a/test/unit/keyring-controller-test.js
+++ /dev/null
@@ -1,172 +0,0 @@
-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(done) {
- 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/linting_test.js b/test/unit/linting_test.js
deleted file mode 100644
index 75d90652d..000000000
--- a/test/unit/linting_test.js
+++ /dev/null
@@ -1,9 +0,0 @@
-// LINTING:
-const lint = require('mocha-eslint');
-const lintPaths = ['app/**/*.js', 'ui/**/*.js', '!node_modules/**', '!dist/**', '!docs/**', '!app/scripts/chromereload.js']
-
-const lintOptions = {
- strict: false,
-}
-
-lint(lintPaths, lintOptions) \ No newline at end of file
diff --git a/test/unit/message-manager-test.js b/test/unit/message-manager-test.js
index faf7429d4..9b76241ed 100644
--- a/test/unit/message-manager-test.js
+++ b/test/unit/message-manager-test.js
@@ -1,29 +1,26 @@
const assert = require('assert')
-const extend = require('xtend')
-const EventEmitter = require('events')
-
const MessageManger = require('../../app/scripts/lib/message-manager')
-describe('Transaction Manager', function() {
+describe('Message Manager', function () {
let messageManager
- beforeEach(function() {
- messageManager = new MessageManger ()
+ beforeEach(function () {
+ messageManager = new MessageManger()
})
- describe('#getMsgList', function() {
- it('when new should return empty array', function() {
+ 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() {
+ it('should also return transactions from local storage if any', function () {
})
})
- describe('#addMsg', function() {
- it('adds a Msg returned in getMsgList', 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
@@ -33,8 +30,8 @@ describe('Transaction Manager', function() {
})
})
- describe('#setMsgStatusApproved', function() {
- it('sets the Msg status to approved', function() {
+ 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)
@@ -45,8 +42,8 @@ describe('Transaction Manager', function() {
})
})
- describe('#rejectMsg', function() {
- it('sets the Msg status to rejected', function() {
+ 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)
@@ -57,8 +54,8 @@ describe('Transaction Manager', function() {
})
})
- describe('#_updateMsg', function() {
- it('replaces the Msg with the same id', function() {
+ 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' })
@@ -67,19 +64,19 @@ describe('Transaction Manager', function() {
})
})
- describe('#getUnapprovedMsgs', function() {
- it('returns unapproved Msgs in a hash', function() {
+ 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()
+ const 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() {
+ 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')
diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js
index 78b9e9df7..3fc7f9a98 100644
--- a/test/unit/metamask-controller-test.js
+++ b/test/unit/metamask-controller-test.js
@@ -3,27 +3,127 @@ const sinon = require('sinon')
const clone = require('clone')
const MetaMaskController = require('../../app/scripts/metamask-controller')
const firstTimeState = require('../../app/scripts/first-time-state')
+const BN = require('ethereumjs-util').BN
+const GWEI_BN = new BN('1000000000')
-const STORAGE_KEY = 'metamask-config'
-
-describe('MetaMaskController', function() {
+describe('MetaMaskController', function () {
const noop = () => {}
- let controller = new MetaMaskController({
+ const metamaskController = new MetaMaskController({
showUnconfirmedMessage: noop,
unlockAccountMessage: noop,
showUnapprovedTx: noop,
+ platform: {},
+ encryptor: {
+ encrypt: function(password, object) {
+ this.object = object
+ return Promise.resolve()
+ },
+ decrypt: function () {
+ return Promise.resolve(this.object)
+ }
+ },
// initial state
initState: clone(firstTimeState),
})
- beforeEach(function() {
+ beforeEach(function () {
// sinon allows stubbing methods that are easily verified
this.sinon = sinon.sandbox.create()
})
- afterEach(function() {
+ afterEach(function () {
// sinon requires cleanup otherwise it will overwrite context
this.sinon.restore()
})
-}) \ No newline at end of file
+ describe('Metamask Controller', function () {
+ assert(metamaskController)
+
+ beforeEach(function () {
+ sinon.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
+ sinon.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
+ })
+
+ afterEach(function () {
+ metamaskController.keyringController.createNewVaultAndKeychain.restore()
+ metamaskController.keyringController.createNewVaultAndRestore.restore()
+ })
+
+ describe('#getGasPrice', function () {
+ it('gives the 50th percentile lowest accepted gas price from recentBlocksController', async function () {
+ const realRecentBlocksController = metamaskController.recentBlocksController
+ metamaskController.recentBlocksController = {
+ store: {
+ getState: () => {
+ return {
+ recentBlocks: [
+ { gasPrices: [ '0x3b9aca00', '0x174876e800'] },
+ { gasPrices: [ '0x3b9aca00', '0x174876e800'] },
+ { gasPrices: [ '0x174876e800', '0x174876e800' ]},
+ { gasPrices: [ '0x174876e800', '0x174876e800' ]},
+ ]
+ }
+ }
+ }
+ }
+
+ const gasPrice = metamaskController.getGasPrice()
+ assert.equal(gasPrice, '0x3b9aca00', 'accurately estimates 50th percentile accepted gas price')
+
+ metamaskController.recentBlocksController = realRecentBlocksController
+ })
+
+ it('gives the 1 gwei price if no blocks have been seen.', async function () {
+ const realRecentBlocksController = metamaskController.recentBlocksController
+ metamaskController.recentBlocksController = {
+ store: {
+ getState: () => {
+ return {
+ recentBlocks: []
+ }
+ }
+ }
+ }
+
+ const gasPrice = metamaskController.getGasPrice()
+ assert.equal(gasPrice, '0x' + GWEI_BN.toString(16), 'defaults to 1 gwei')
+
+ metamaskController.recentBlocksController = realRecentBlocksController
+ })
+
+ })
+
+ describe('#createNewVaultAndKeychain', function () {
+ it('can only create new vault on keyringController once', async function () {
+ const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
+
+
+ const password = 'a-fake-password'
+
+ const first = await metamaskController.createNewVaultAndKeychain(password)
+ const second = await metamaskController.createNewVaultAndKeychain(password)
+
+ assert(metamaskController.keyringController.createNewVaultAndKeychain.calledOnce)
+
+ selectStub.reset()
+ })
+ })
+
+ describe('#createNewVaultAndRestore', function () {
+ it('should be able to call newVaultAndRestore despite a mistake.', async function () {
+ // const selectStub = sinon.stub(metamaskController, 'selectFirstIdentity')
+
+ const password = 'what-what-what'
+ const wrongSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadiu'
+ const rightSeed = 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'
+ const first = await metamaskController.createNewVaultAndRestore(password, wrongSeed)
+ .catch((e) => {
+ return
+ })
+ const second = await metamaskController.createNewVaultAndRestore(password, rightSeed)
+
+ assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice)
+ })
+ })
+ })
+})
diff --git a/test/unit/migrations-test.js b/test/unit/migrations-test.js
index ccd1477b0..5bad25a45 100644
--- a/test/unit/migrations-test.js
+++ b/test/unit/migrations-test.js
@@ -3,7 +3,7 @@ const path = require('path')
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
+let vault5, vault6, vault7, vault8, vault9 // vault10, vault11
const migration2 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '002'))
const migration3 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '003'))
@@ -16,6 +16,7 @@ const migration9 = require(path.join('..', '..', 'app', 'scripts', 'migrations',
const migration10 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '010'))
const migration11 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '011'))
const migration12 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '012'))
+const migration13 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '013'))
const oldTestRpc = 'https://rawtestrpc.metamask.io/'
@@ -23,7 +24,6 @@ const newTestRpc = 'https://testrpc.metamask.io/'
describe('wallet1 is migrated successfully', () => {
it('should convert providers', () => {
-
wallet1.data.config.provider = { type: 'etherscan', rpcTarget: null }
return migration2.migrate(wallet1)
@@ -98,7 +98,11 @@ describe('wallet1 is migrated successfully', () => {
}).then((twelfthResult) => {
assert.equal(twelfthResult.data.NoticeController.noticesList[0].body, '', 'notices that have been read should have an empty body.')
assert.equal(twelfthResult.data.NoticeController.noticesList[1].body, 'nonempty', 'notices that have not been read should not have an empty body.')
- })
+ assert.equal(twelfthResult.data.config.provider.type, 'testnet', 'network is originally testnet.')
+ return migration13.migrate(twelfthResult)
+ }).then((thirteenthResult) => {
+ assert.equal(thirteenthResult.data.config.provider.type, 'ropsten', 'network has been changed to ropsten.')
+ })
})
})
diff --git a/test/unit/migrations/021-test.js b/test/unit/migrations/021-test.js
new file mode 100644
index 000000000..458e9b4b5
--- /dev/null
+++ b/test/unit/migrations/021-test.js
@@ -0,0 +1,16 @@
+const assert = require('assert')
+
+const wallet2 = require('../../lib/migrations/002.json')
+const migration21 = require('../../../app/scripts/migrations/021')
+
+describe('wallet2 is migrated successfully with out the BlacklistController', () => {
+ it('should delete BlacklistController key', (done) => {
+ migration21.migrate(wallet2)
+ .then((migratedData) => {
+ assert.equal(migratedData.meta.version, 21)
+ assert(!migratedData.data.BlacklistController)
+ assert(!migratedData.data.RecentBlocks)
+ done()
+ }).catch(done)
+ })
+})
diff --git a/test/unit/migrator-test.js b/test/unit/migrator-test.js
new file mode 100644
index 000000000..16066fefe
--- /dev/null
+++ b/test/unit/migrator-test.js
@@ -0,0 +1,41 @@
+const assert = require('assert')
+const clone = require('clone')
+const Migrator = require('../../app/scripts/lib/migrator/')
+const migrations = [
+ {
+ version: 1,
+ migrate: (data) => {
+ // clone the data just like we do in migrations
+ const clonedData = clone(data)
+ clonedData.meta.version = 1
+ return Promise.resolve(clonedData)
+ },
+ },
+ {
+ version: 2,
+ migrate: (data) => {
+ const clonedData = clone(data)
+ clonedData.meta.version = 2
+ return Promise.resolve(clonedData)
+ },
+ },
+ {
+ version: 3,
+ migrate: (data) => {
+ const clonedData = clone(data)
+ clonedData.meta.version = 3
+ return Promise.resolve(clonedData)
+ },
+ },
+]
+const versionedData = {meta: {version: 0}, data: {hello: 'world'}}
+describe('Migrator', () => {
+ const migrator = new Migrator({ migrations })
+ it('migratedData version should be version 3', (done) => {
+ migrator.migrateData(versionedData)
+ .then((migratedData) => {
+ assert.equal(migratedData.meta.version, migrations[2].version)
+ done()
+ }).catch(done)
+ })
+})
diff --git a/test/unit/nameForAccount_test.js b/test/unit/nameForAccount_test.js
index 6839d40f8..e7c0b18b4 100644
--- a/test/unit/nameForAccount_test.js
+++ b/test/unit/nameForAccount_test.js
@@ -4,25 +4,23 @@ var sinon = require('sinon')
var path = require('path')
var contractNamer = require(path.join(__dirname, '..', '..', 'ui', 'lib', 'contract-namer.js'))
-describe('contractNamer', function() {
-
- beforeEach(function() {
+describe('contractNamer', function () {
+ beforeEach(function () {
this.sinon = sinon.sandbox.create()
})
- afterEach(function() {
+ afterEach(function () {
this.sinon.restore()
})
- describe('naming a contract', function() {
-
- it('should return nothing for an unknown random account', function() {
+ describe('naming a contract', function () {
+ it('should return nothing for an unknown random account', function () {
const input = '0x2386F26FC10000'
const output = contractNamer(input)
assert.deepEqual(output, null)
})
- it('should accept identities as an optional second parameter', function() {
+ it('should accept identities as an optional second parameter', function () {
const input = '0x2386F26FC10000'.toLowerCase()
const expected = 'bar'
const identities = {}
@@ -31,7 +29,7 @@ describe('contractNamer', function() {
assert.deepEqual(output, expected)
})
- it('should check for identities case insensitively', function() {
+ it('should check for identities case insensitively', function () {
const input = '0x2386F26FC10000'.toLowerCase()
const expected = 'bar'
const identities = {}
@@ -39,6 +37,5 @@ describe('contractNamer', function() {
const output = contractNamer(input.toUpperCase(), identities)
assert.deepEqual(output, expected)
})
-
})
})
diff --git a/test/unit/network-contoller-test.js b/test/unit/network-contoller-test.js
new file mode 100644
index 000000000..0b3b5adeb
--- /dev/null
+++ b/test/unit/network-contoller-test.js
@@ -0,0 +1,84 @@
+const assert = require('assert')
+const NetworkController = require('../../app/scripts/controllers/network')
+
+describe('# Network Controller', function () {
+ let networkController
+ const networkControllerProviderInit = {
+ getAccounts: () => {},
+ }
+
+ beforeEach(function () {
+ networkController = new NetworkController({
+ provider: {
+ type: 'rinkeby',
+ },
+ })
+
+ networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor)
+ })
+ describe('network', function () {
+ describe('#provider', function () {
+ it('provider should be updatable without reassignment', function () {
+ networkController.initializeProvider(networkControllerProviderInit, dummyProviderConstructor)
+ const proxy = networkController._proxy
+ proxy.setTarget({ test: true, on: () => {} })
+ assert.ok(proxy.test)
+ })
+ })
+ describe('#getNetworkState', function () {
+ it('should return loading when new', function () {
+ const networkState = networkController.getNetworkState()
+ assert.equal(networkState, 'loading', 'network is loading')
+ })
+ })
+
+ describe('#setNetworkState', function () {
+ it('should update the network', function () {
+ networkController.setNetworkState(1)
+ const networkState = networkController.getNetworkState()
+ assert.equal(networkState, 1, 'network is 1')
+ })
+ })
+
+ describe('#getRpcAddressForType', function () {
+ it('should return the right rpc address', function () {
+ const rpcTarget = networkController.getRpcAddressForType('mainnet')
+ assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress')
+ })
+ })
+ describe('#setProviderType', function () {
+ it('should update provider.type', function () {
+ networkController.setProviderType('mainnet')
+ const type = networkController.getProviderConfig().type
+ assert.equal(type, 'mainnet', 'provider type is updated')
+ })
+ it('should set the network to loading', function () {
+ networkController.setProviderType('mainnet')
+ const loading = networkController.isNetworkLoading()
+ assert.ok(loading, 'network is loading')
+ })
+ it('should set the right rpcTarget', function () {
+ networkController.setProviderType('mainnet')
+ const rpcTarget = networkController.getProviderConfig().rpcTarget
+ assert.equal(rpcTarget, 'https://mainnet.infura.io/metamask', 'returns the right rpcAddress')
+ })
+ })
+ })
+})
+
+function dummyProviderConstructor() {
+ return {
+ // provider
+ sendAsync: noop,
+ // block tracker
+ _blockTracker: {},
+ start: noop,
+ stop: noop,
+ on: noop,
+ addListener: noop,
+ once: noop,
+ removeAllListeners: noop,
+ }
+}
+
+function noop() {} \ No newline at end of file
diff --git a/test/unit/nodeify-test.js b/test/unit/nodeify-test.js
index a14d34338..c7b127889 100644
--- a/test/unit/nodeify-test.js
+++ b/test/unit/nodeify-test.js
@@ -1,22 +1,30 @@
const assert = require('assert')
const nodeify = require('../../app/scripts/lib/nodeify')
-describe('nodeify', function() {
-
+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)
+ it('should retain original context', function (done) {
+ var nodified = nodeify(obj.promiseFunc, obj)
nodified('baz', function (err, res) {
assert.equal(res, 'barbaz')
done()
})
})
+ it('should allow the last argument to not be a function', function (done) {
+ const nodified = nodeify(obj.promiseFunc, obj)
+ try {
+ nodified('baz')
+ done()
+ } catch (err) {
+ done(new Error('should not have thrown if the last argument is not a function'))
+ }
+ })
})
diff --git a/test/unit/nonce-tracker-test.js b/test/unit/nonce-tracker-test.js
new file mode 100644
index 000000000..8970cf84d
--- /dev/null
+++ b/test/unit/nonce-tracker-test.js
@@ -0,0 +1,203 @@
+const assert = require('assert')
+const NonceTracker = require('../../app/scripts/lib/nonce-tracker')
+const MockTxGen = require('../lib/mock-tx-gen')
+let providerResultStub = {}
+
+describe('Nonce Tracker', function () {
+ let nonceTracker, provider
+ let getPendingTransactions, pendingTxs
+ let getConfirmedTransactions, confirmedTxs
+
+ describe('#getNonceLock', function () {
+
+ describe('with 3 confirmed and 1 pending', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 })
+ pendingTxs = txGen.generate({ status: 'submitted' }, { count: 1 })
+ nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x1')
+ })
+
+ it('should return 4', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '4', `nonce should be 4 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+
+ it('should use localNonce if network returns a nonce lower then a confirmed tx in state', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '4', 'nonce should be 4')
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('with no previous txs', function () {
+ beforeEach(function () {
+ nonceTracker = generateNonceTrackerWith([], [])
+ })
+
+ it('should return 0', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 returned ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('with multiple previous txs with same nonce', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 1 })
+ pendingTxs = txGen.generate({
+ status: 'submitted',
+ txParams: { nonce: '0x01' },
+ }, { count: 5 })
+
+ nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x0')
+ })
+
+ it('should return nonce after those', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('when local confirmed count is higher than network nonce', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 3 })
+ nonceTracker = generateNonceTrackerWith([], confirmedTxs, '0x1')
+ })
+
+ it('should return nonce after those', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '3', `nonce should be 3 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('when local pending count is higher than other metrics', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 })
+ nonceTracker = generateNonceTrackerWith(pendingTxs, [])
+ })
+
+ it('should return nonce after those', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '2', `nonce should be 2 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('when provider nonce is higher than other metrics', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ pendingTxs = txGen.generate({ status: 'submitted' }, { count: 2 })
+ nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x05')
+ })
+
+ it('should return nonce after those', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('when there are some pending nonces below the remote one and some over.', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 })
+ nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x03')
+ })
+
+ it('should return nonce after those', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '5', `nonce should be 5 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('when there are pending nonces non sequentially over the network nonce.', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ txGen.generate({ status: 'submitted' }, { count: 5 })
+ // 5 over that number
+ pendingTxs = txGen.generate({ status: 'submitted' }, { count: 5 })
+ nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x00')
+ })
+
+ it('should return nonce after network nonce', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '0', `nonce should be 0 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('When all three return different values', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ const confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 10 })
+ const pendingTxs = txGen.generate({
+ status: 'submitted',
+ nonce: 100,
+ }, { count: 1 })
+ // 0x32 is 50 in hex:
+ nonceTracker = generateNonceTrackerWith(pendingTxs, confirmedTxs, '0x32')
+ })
+
+ it('should return nonce after network nonce', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '50', `nonce should be 50 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+
+ describe('Faq issue 67', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ const confirmedTxs = txGen.generate({ status: 'confirmed' }, { count: 64 })
+ const pendingTxs = txGen.generate({
+ status: 'submitted',
+ }, { count: 10 })
+ // 0x40 is 64 in hex:
+ nonceTracker = generateNonceTrackerWith(pendingTxs, [], '0x40')
+ })
+
+ it('should return nonce after network nonce', async function () {
+ this.timeout(15000)
+ const nonceLock = await nonceTracker.getNonceLock('0x7d3517b0d011698406d6e0aed8453f0be2697926')
+ assert.equal(nonceLock.nextNonce, '74', `nonce should be 74 got ${nonceLock.nextNonce}`)
+ await nonceLock.releaseLock()
+ })
+ })
+ })
+})
+
+function generateNonceTrackerWith (pending, confirmed, providerStub = '0x0') {
+ const getPendingTransactions = () => pending
+ const getConfirmedTransactions = () => confirmed
+ providerResultStub.result = providerStub
+ const provider = {
+ sendAsync: (_, cb) => { cb(undefined, providerResultStub) },
+ _blockTracker: {
+ getCurrentBlock: () => '0x11b568',
+ },
+ }
+ return new NonceTracker({
+ provider,
+ getPendingTransactions,
+ getConfirmedTransactions,
+ })
+}
+
diff --git a/test/unit/notice-controller-test.js b/test/unit/notice-controller-test.js
index ea37108bb..09eeda15c 100644
--- a/test/unit/notice-controller-test.js
+++ b/test/unit/notice-controller-test.js
@@ -1,42 +1,38 @@
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-persistence-key'
-describe('notice-controller', function() {
+describe('notice-controller', function () {
var noticeController
- beforeEach(function() {
+ beforeEach(function () {
// simple localStorage polyfill
- let configManager = configManagerGen()
+ const configManager = configManagerGen()
noticeController = new NoticeController({
configManager: configManager,
})
})
- describe('notices', function() {
- describe('#getNoticesList', function() {
- it('should return an empty array when new', function(done) {
- var testList = [{
- id:0,
- read:false,
- title:"Futuristic Notice"
- }]
+ describe('notices', function () {
+ describe('#getNoticesList', function () {
+ it('should return an empty array when new', function (done) {
+ // const testList = [{
+ // id: 0,
+ // read: false,
+ // title: 'Futuristic Notice',
+ // }]
var result = noticeController.getNoticesList()
assert.equal(result.length, 0)
done()
})
})
- describe('#setNoticesList', function() {
+ describe('#setNoticesList', function () {
it('should set data appropriately', function (done) {
var testList = [{
- id:0,
- read:false,
- title:"Futuristic Notice"
+ id: 0,
+ read: false,
+ title: 'Futuristic Notice',
}]
noticeController.setNoticesList(testList)
var testListId = noticeController.getNoticesList()[0].id
@@ -45,12 +41,12 @@ describe('notice-controller', function() {
})
})
- describe('#updateNoticeslist', function() {
- it('should integrate the latest changes from the source', function(done) {
+ describe('#updateNoticeslist', function () {
+ it('should integrate the latest changes from the source', function (done) {
var testList = [{
- id:55,
- read:false,
- title:"Futuristic Notice"
+ id: 55,
+ read: false,
+ title: 'Futuristic Notice',
}]
noticeController.setNoticesList(testList)
noticeController.updateNoticesList().then(() => {
@@ -62,14 +58,14 @@ describe('notice-controller', function() {
})
it('should not overwrite any existing fields', function (done) {
var testList = [{
- id:0,
- read:false,
- title:"Futuristic Notice"
+ id: 0,
+ read: false,
+ title: 'Futuristic Notice',
}]
noticeController.setNoticesList(testList)
var newList = noticeController.getNoticesList()
assert.equal(newList[0].id, 0)
- assert.equal(newList[0].title, "Futuristic Notice")
+ assert.equal(newList[0].title, 'Futuristic Notice')
assert.equal(newList.length, 1)
done()
})
@@ -78,9 +74,9 @@ describe('notice-controller', function() {
describe('#markNoticeRead', function () {
it('should mark a notice as read', function (done) {
var testList = [{
- id:0,
- read:false,
- title:"Futuristic Notice"
+ id: 0,
+ read: false,
+ title: 'Futuristic Notice',
}]
noticeController.setNoticesList(testList)
noticeController.markNoticeRead(testList[0])
@@ -93,9 +89,9 @@ describe('notice-controller', function() {
describe('#getLatestUnreadNotice', function () {
it('should retrieve the latest unread notice', function (done) {
var testList = [
- {id:0,read:true,title:"Past Notice"},
- {id:1,read:false,title:"Current Notice"},
- {id:2,read:false,title:"Future Notice"},
+ {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()
@@ -104,9 +100,9 @@ describe('notice-controller', function() {
})
it('should return undefined if no unread notices exist.', function (done) {
var testList = [
- {id:0,read:true,title:"Past Notice"},
- {id:1,read:true,title:"Current Notice"},
- {id:2,read:true,title:"Future Notice"},
+ {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()
@@ -115,5 +111,4 @@ describe('notice-controller', function() {
})
})
})
-
})
diff --git a/test/unit/pending-balance-test.js b/test/unit/pending-balance-test.js
new file mode 100644
index 000000000..dc4c1c3e4
--- /dev/null
+++ b/test/unit/pending-balance-test.js
@@ -0,0 +1,93 @@
+const assert = require('assert')
+const PendingBalanceCalculator = require('../../app/scripts/lib/pending-balance-calculator')
+const MockTxGen = require('../lib/mock-tx-gen')
+const BN = require('ethereumjs-util').BN
+let providerResultStub = {}
+
+const zeroBn = new BN(0)
+const etherBn = new BN(String(1e18))
+const ether = '0x' + etherBn.toString(16)
+
+describe('PendingBalanceCalculator', function () {
+ let balanceCalculator, pendingTxs
+
+ describe('#calculateMaxCost(tx)', function () {
+ it('returns a BN for a given tx value', function () {
+ const txGen = new MockTxGen()
+ pendingTxs = txGen.generate({
+ status: 'submitted',
+ txParams: {
+ value: ether,
+ gasPrice: '0x0',
+ gas: '0x0',
+ }
+ }, { count: 1 })
+
+ const balanceCalculator = generateBalanceCalcWith([], zeroBn)
+ const result = balanceCalculator.calculateMaxCost(pendingTxs[0])
+ assert.equal(result.toString(), etherBn.toString(), 'computes one ether')
+ })
+
+ it('calculates gas costs as well', function () {
+ const txGen = new MockTxGen()
+ pendingTxs = txGen.generate({
+ status: 'submitted',
+ txParams: {
+ value: '0x0',
+ gasPrice: '0x2',
+ gas: '0x3',
+ }
+ }, { count: 1 })
+
+ const balanceCalculator = generateBalanceCalcWith([], zeroBn)
+ const result = balanceCalculator.calculateMaxCost(pendingTxs[0])
+ assert.equal(result.toString(), '6', 'computes 6 wei of gas')
+ })
+ })
+
+ describe('if you have no pending txs and one ether', function () {
+
+ beforeEach(function () {
+ balanceCalculator = generateBalanceCalcWith([], etherBn)
+ })
+
+ it('returns the network balance', async function () {
+ const result = await balanceCalculator.getBalance()
+ assert.equal(result, ether, `gave ${result} needed ${ether}`)
+ })
+ })
+
+ describe('if you have a one ether pending tx and one ether', function () {
+ beforeEach(function () {
+ const txGen = new MockTxGen()
+ pendingTxs = txGen.generate({
+ status: 'submitted',
+ txParams: {
+ value: ether,
+ gasPrice: '0x0',
+ gas: '0x0',
+ }
+ }, { count: 1 })
+
+ balanceCalculator = generateBalanceCalcWith(pendingTxs, etherBn)
+ })
+
+ it('returns the subtracted result', async function () {
+ const result = await balanceCalculator.getBalance()
+ assert.equal(result, '0x0', `gave ${result} needed '0x0'`)
+ return true
+ })
+
+ })
+})
+
+function generateBalanceCalcWith (transactions, providerStub = zeroBn) {
+ const getPendingTransactions = async () => transactions
+ const getBalance = async () => providerStub
+
+ return new PendingBalanceCalculator({
+ getBalance,
+ getPendingTransactions,
+ })
+}
+
diff --git a/test/unit/pending-tx-test.js b/test/unit/pending-tx-test.js
new file mode 100644
index 000000000..f0b4e3bfc
--- /dev/null
+++ b/test/unit/pending-tx-test.js
@@ -0,0 +1,402 @@
+const assert = require('assert')
+const ethUtil = require('ethereumjs-util')
+const EthTx = require('ethereumjs-tx')
+const ObservableStore = require('obs-store')
+const clone = require('clone')
+const { createTestProviderTools } = require('../stub/provider')
+const PendingTransactionTracker = require('../../app/scripts/lib/pending-tx-tracker')
+const MockTxGen = require('../lib/mock-tx-gen')
+const sinon = require('sinon')
+const noop = () => true
+const currentNetworkId = 42
+const otherNetworkId = 36
+const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex')
+
+
+describe('PendingTransactionTracker', function () {
+ let pendingTxTracker, txMeta, txMetaNoHash, txMetaNoRawTx, providerResultStub,
+ provider, txMeta3, txList, knownErrors
+ this.timeout(10000)
+ beforeEach(function () {
+ txMeta = {
+ id: 1,
+ hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
+ status: 'signed',
+ txParams: {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ nonce: '0x1',
+ value: '0xfffff',
+ },
+ rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
+ }
+ txMetaNoHash = {
+ id: 2,
+ status: 'signed',
+ txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'},
+ }
+ txMetaNoRawTx = {
+ hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
+ status: 'signed',
+ txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'},
+ }
+ providerResultStub = {}
+ provider = createTestProviderTools({ scaffold: providerResultStub }).provider
+
+ pendingTxTracker = new PendingTransactionTracker({
+ provider,
+ nonceTracker: {
+ getGlobalLock: async () => {
+ return { releaseLock: () => {} }
+ }
+ },
+ getPendingTransactions: () => {return []},
+ getCompletedTransactions: () => {return []},
+ publishTransaction: () => {},
+ })
+ })
+
+ describe('_checkPendingTx state management', function () {
+ let stub
+
+ afterEach(function () {
+ if (stub) {
+ stub.restore()
+ }
+ })
+
+ it('should become failed if another tx with the same nonce succeeds', async function () {
+
+ // SETUP
+ const txGen = new MockTxGen()
+
+ txGen.generate({
+ id: '456',
+ value: '0x01',
+ hash: '0xbad',
+ status: 'confirmed',
+ nonce: '0x01',
+ }, { count: 1 })
+
+ const pending = txGen.generate({
+ id: '123',
+ value: '0x02',
+ hash: '0xfad',
+ status: 'submitted',
+ nonce: '0x01',
+ }, { count: 1 })[0]
+
+ stub = sinon.stub(pendingTxTracker, 'getCompletedTransactions')
+ .returns(txGen.txs)
+
+ // THE EXPECTATION
+ const spy = sinon.spy()
+ pendingTxTracker.on('tx:failed', (txId, err) => {
+ assert.equal(txId, pending.id, 'should fail the pending tx')
+ assert.equal(err.name, 'NonceTakenErr', 'should emit a nonce taken error.')
+ spy(txId, err)
+ })
+
+ // THE METHOD
+ await pendingTxTracker._checkPendingTx(pending)
+
+ // THE ASSERTION
+ assert.ok(spy.calledWith(pending.id), 'tx failed should be emitted')
+ })
+ })
+
+ describe('#checkForTxInBlock', function () {
+ it('should return if no pending transactions', function () {
+ // throw a type error if it trys to do anything on the block
+ // thus failing the test
+ const block = Proxy.revocable({}, {}).revoke()
+ pendingTxTracker.checkForTxInBlock(block)
+ })
+ it('should emit \'tx:failed\' if the txMeta does not have a hash', function (done) {
+ const block = Proxy.revocable({}, {}).revoke()
+ pendingTxTracker.getPendingTransactions = () => [txMetaNoHash]
+ pendingTxTracker.once('tx:failed', (txId, err) => {
+ assert(txId, txMetaNoHash.id, 'should pass txId')
+ done()
+ })
+ pendingTxTracker.checkForTxInBlock(block)
+ })
+ it('should emit \'txConfirmed\' if the tx is in the block', function (done) {
+ const block = { transactions: [txMeta]}
+ pendingTxTracker.getPendingTransactions = () => [txMeta]
+ pendingTxTracker.once('tx:confirmed', (txId) => {
+ assert(txId, txMeta.id, 'should pass txId')
+ done()
+ })
+ pendingTxTracker.once('tx:failed', (_, err) => { done(err) })
+ pendingTxTracker.checkForTxInBlock(block)
+ })
+ })
+ describe('#queryPendingTxs', function () {
+ it('should call #_checkPendingTxs if their is no oldBlock', function (done) {
+ let newBlock, oldBlock
+ newBlock = { number: '0x01' }
+ pendingTxTracker._checkPendingTxs = done
+ pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
+ })
+ it('should call #_checkPendingTxs if oldBlock and the newBlock have a diff of greater then 1', function (done) {
+ let newBlock, oldBlock
+ oldBlock = { number: '0x01' }
+ newBlock = { number: '0x03' }
+ pendingTxTracker._checkPendingTxs = done
+ pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
+ })
+ it('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less', function (done) {
+ let newBlock, oldBlock
+ oldBlock = { number: '0x1' }
+ newBlock = { number: '0x2' }
+ pendingTxTracker._checkPendingTxs = () => {
+ const err = new Error('should not call #_checkPendingTxs if oldBlock and the newBlock have a diff of 1 or less')
+ done(err)
+ }
+ pendingTxTracker.queryPendingTxs({ oldBlock, newBlock })
+ done()
+ })
+ })
+
+ describe('#_checkPendingTx', function () {
+ it('should emit \'tx:failed\' if the txMeta does not have a hash', function (done) {
+ pendingTxTracker.once('tx:failed', (txId, err) => {
+ assert(txId, txMetaNoHash.id, 'should pass txId')
+ done()
+ })
+ pendingTxTracker._checkPendingTx(txMetaNoHash)
+ })
+
+ it('should should return if query does not return txParams', function () {
+ providerResultStub.eth_getTransactionByHash = null
+ pendingTxTracker._checkPendingTx(txMeta)
+ })
+
+ it('should emit \'txConfirmed\'', function (done) {
+ providerResultStub.eth_getTransactionByHash = {blockNumber: '0x01'}
+ pendingTxTracker.once('tx:confirmed', (txId) => {
+ assert(txId, txMeta.id, 'should pass txId')
+ done()
+ })
+ pendingTxTracker.once('tx:failed', (_, err) => { done(err) })
+ pendingTxTracker._checkPendingTx(txMeta)
+ })
+ })
+
+ describe('#_checkPendingTxs', function () {
+ beforeEach(function () {
+ const txMeta2 = txMeta3 = txMeta
+ txMeta2.id = 2
+ txMeta3.id = 3
+ txList = [txMeta, txMeta2, txMeta3].map((tx) => {
+ tx.processed = new Promise ((resolve) => { tx.resolve = resolve })
+ return tx
+ })
+ })
+
+ it('should warp all txMeta\'s in #_checkPendingTx', function (done) {
+ pendingTxTracker.getPendingTransactions = () => txList
+ pendingTxTracker._checkPendingTx = (tx) => { tx.resolve(tx) }
+ const list = txList.map
+ Promise.all(txList.map((tx) => tx.processed))
+ .then((txCompletedList) => done())
+ .catch(done)
+
+ pendingTxTracker._checkPendingTxs()
+ })
+ })
+
+ describe('#resubmitPendingTxs', function () {
+ const blockStub = { number: '0x0' };
+ beforeEach(function () {
+ const txMeta2 = txMeta3 = txMeta
+ txList = [txMeta, txMeta2, txMeta3].map((tx) => {
+ tx.processed = new Promise ((resolve) => { tx.resolve = resolve })
+ return tx
+ })
+ })
+
+ it('should return if no pending transactions', function () {
+ pendingTxTracker.resubmitPendingTxs()
+ })
+ it('should call #_resubmitTx for all pending tx\'s', function (done) {
+ pendingTxTracker.getPendingTransactions = () => txList
+ pendingTxTracker._resubmitTx = async (tx) => { tx.resolve(tx) }
+ Promise.all(txList.map((tx) => tx.processed))
+ .then((txCompletedList) => done())
+ .catch(done)
+ pendingTxTracker.resubmitPendingTxs(blockStub)
+ })
+ it('should not emit \'tx:failed\' if the txMeta throws a known txError', function (done) {
+ knownErrors =[
+ // geth
+ ' Replacement transaction Underpriced ',
+ ' known transaction',
+ // parity
+ 'Gas price too low to replace ',
+ ' transaction with the sAme hash was already imported',
+ // other
+ ' gateway timeout',
+ ' noncE too low ',
+ ]
+ const enoughForAllErrors = txList.concat(txList)
+
+ pendingTxTracker.on('tx:failed', (_, err) => done(err))
+
+ pendingTxTracker.getPendingTransactions = () => enoughForAllErrors
+ pendingTxTracker._resubmitTx = async (tx) => {
+ tx.resolve()
+ throw new Error(knownErrors.pop())
+ }
+ Promise.all(txList.map((tx) => tx.processed))
+ .then((txCompletedList) => done())
+ .catch(done)
+
+ pendingTxTracker.resubmitPendingTxs(blockStub)
+ })
+ it('should emit \'tx:warning\' if it encountered a real error', function (done) {
+ pendingTxTracker.once('tx:warning', (txMeta, err) => {
+ if (err.message === 'im some real error') {
+ const matchingTx = txList.find(tx => tx.id === txMeta.id)
+ matchingTx.resolve()
+ } else {
+ done(err)
+ }
+ })
+
+ pendingTxTracker.getPendingTransactions = () => txList
+ pendingTxTracker._resubmitTx = async (tx) => { throw new TypeError('im some real error') }
+ Promise.all(txList.map((tx) => tx.processed))
+ .then((txCompletedList) => done())
+ .catch(done)
+
+ pendingTxTracker.resubmitPendingTxs(blockStub)
+ })
+ })
+ describe('#_resubmitTx', function () {
+ const mockFirstRetryBlockNumber = '0x1'
+ let txMetaToTestExponentialBackoff
+
+ beforeEach(() => {
+ pendingTxTracker.getBalance = (address) => {
+ assert.equal(address, txMeta.txParams.from, 'Should pass the address')
+ return enoughBalance
+ }
+ pendingTxTracker.publishTransaction = async (rawTx) => {
+ assert.equal(rawTx, txMeta.rawTx, 'Should pass the rawTx')
+ }
+ sinon.spy(pendingTxTracker, 'publishTransaction')
+
+ txMetaToTestExponentialBackoff = Object.assign({}, txMeta, {
+ retryCount: 4,
+ firstRetryBlockNumber: mockFirstRetryBlockNumber,
+ })
+ })
+
+ afterEach(() => {
+ pendingTxTracker.publishTransaction.reset()
+ })
+
+ it('should publish the transaction', function (done) {
+ const enoughBalance = '0x100000'
+
+ // Stubbing out current account state:
+ // Adding the fake tx:
+ pendingTxTracker._resubmitTx(txMeta)
+ .then(() => done())
+ .catch((err) => {
+ assert.ifError(err, 'should not throw an error')
+ done(err)
+ })
+
+ assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction')
+ })
+
+ it('should not publish the transaction if the limit of retries has been exceeded', function (done) {
+ const enoughBalance = '0x100000'
+ const mockLatestBlockNumber = '0x5'
+
+ pendingTxTracker._resubmitTx(txMetaToTestExponentialBackoff, mockLatestBlockNumber)
+ .then(() => done())
+ .catch((err) => {
+ assert.ifError(err, 'should not throw an error')
+ done(err)
+ })
+
+ assert.equal(pendingTxTracker.publishTransaction.callCount, 0, 'Should NOT call publish transaction')
+ })
+
+ it('should publish the transaction if the number of blocks since last retry exceeds the last set limit', function (done) {
+ const enoughBalance = '0x100000'
+ const mockLatestBlockNumber = '0x11'
+
+ pendingTxTracker._resubmitTx(txMetaToTestExponentialBackoff, mockLatestBlockNumber)
+ .then(() => done())
+ .catch((err) => {
+ assert.ifError(err, 'should not throw an error')
+ done(err)
+ })
+
+ assert.equal(pendingTxTracker.publishTransaction.callCount, 1, 'Should call publish transaction')
+ })
+ })
+
+ describe('#_checkIfNonceIsTaken', function () {
+ beforeEach ( function () {
+ let confirmedTxList = [{
+ id: 1,
+ hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
+ status: 'confirmed',
+ txParams: {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ nonce: '0x1',
+ value: '0xfffff',
+ },
+ rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
+ }, {
+ id: 2,
+ hash: '0x0593ee121b92e10d63150ad08b4b8f9c7857d1bd160195ee648fb9a0f8d00eeb',
+ status: 'confirmed',
+ txParams: {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ nonce: '0x2',
+ value: '0xfffff',
+ },
+ rawTx: '0xf86c808504a817c800827b0d940c62bb85faa3311a998d3aba8098c1235c564966880de0b6b3a7640000802aa08ff665feb887a25d4099e40e11f0fef93ee9608f404bd3f853dd9e84ed3317a6a02ec9d3d1d6e176d4d2593dd760e74ccac753e6a0ea0d00cc9789d0d7ff1f471d',
+ }]
+ pendingTxTracker.getCompletedTransactions = (address) => {
+ if (!address) throw new Error('unless behavior has changed #_checkIfNonceIsTaken needs a filtered list of transactions to see if the nonce is taken')
+ return confirmedTxList
+ }
+ })
+
+ it('should return false if nonce has not been taken', function (done) {
+ pendingTxTracker._checkIfNonceIsTaken({
+ txParams: {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ nonce: '0x3',
+ value: '0xfffff',
+ },
+ })
+ .then((taken) => {
+ assert.ok(!taken)
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should return true if nonce has been taken', function (done) {
+ pendingTxTracker._checkIfNonceIsTaken({
+ txParams: {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ nonce: '0x2',
+ value: '0xfffff',
+ },
+ }).then((taken) => {
+ assert.ok(taken)
+ done()
+ })
+ .catch(done)
+ })
+ })
+})
diff --git a/test/unit/personal-message-manager-test.js b/test/unit/personal-message-manager-test.js
index f2c01392c..ec2f9a4d1 100644
--- a/test/unit/personal-message-manager-test.js
+++ b/test/unit/personal-message-manager-test.js
@@ -1,29 +1,27 @@
const assert = require('assert')
-const extend = require('xtend')
-const EventEmitter = require('events')
const PersonalMessageManager = require('../../app/scripts/lib/personal-message-manager')
-describe('Personal Message Manager', function() {
+describe('Personal Message Manager', function () {
let messageManager
- beforeEach(function() {
+ beforeEach(function () {
messageManager = new PersonalMessageManager()
})
- describe('#getMsgList', function() {
- it('when new should return empty array', function() {
+ 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() {
+ it('should also return transactions from local storage if any', function () {
})
})
- describe('#addMsg', function() {
- it('adds a Msg returned in getMsgList', 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
@@ -33,8 +31,8 @@ describe('Personal Message Manager', function() {
})
})
- describe('#setMsgStatusApproved', function() {
- it('sets the Msg status to approved', function() {
+ 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)
@@ -45,8 +43,8 @@ describe('Personal Message Manager', function() {
})
})
- describe('#rejectMsg', function() {
- it('sets the Msg status to rejected', function() {
+ 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)
@@ -57,8 +55,8 @@ describe('Personal Message Manager', function() {
})
})
- describe('#_updateMsg', function() {
- it('replaces the Msg with the same id', function() {
+ 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' })
@@ -67,19 +65,19 @@ describe('Personal Message Manager', function() {
})
})
- describe('#getUnapprovedMsgs', function() {
- it('returns unapproved Msgs in a hash', function() {
+ 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()
+ const 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() {
+ 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')
@@ -87,24 +85,23 @@ describe('Personal Message Manager', function() {
})
})
- describe('#normalizeMsgData', function() {
- it('converts text to a utf8 hex string', function() {
+ describe('#normalizeMsgData', function () {
+ it('converts text to a utf8 hex string', function () {
var input = 'hello'
var output = messageManager.normalizeMsgData(input)
assert.equal(output, '0x68656c6c6f', 'predictably hex encoded')
})
- it('tolerates a hex prefix', function() {
+ it('tolerates a hex prefix', function () {
var input = '0x12'
var output = messageManager.normalizeMsgData(input)
assert.equal(output, '0x12', 'un modified')
})
- it('tolerates normal hex', function() {
+ it('tolerates normal hex', function () {
var input = '12'
var output = messageManager.normalizeMsgData(input)
assert.equal(output, '0x12', 'adds prefix')
})
})
-
})
diff --git a/test/unit/preferences-controller-test.js b/test/unit/preferences-controller-test.js
new file mode 100644
index 000000000..9fb5e4251
--- /dev/null
+++ b/test/unit/preferences-controller-test.js
@@ -0,0 +1,48 @@
+const assert = require('assert')
+const PreferencesController = require('../../app/scripts/controllers/preferences')
+
+describe('preferences controller', function () {
+ let preferencesController
+
+ before(() => {
+ preferencesController = new PreferencesController()
+ })
+
+ describe('addToken', function () {
+ it('should add that token to its state', async function () {
+ const address = '0xabcdef1234567'
+ const symbol = 'ABBR'
+ const decimals = 5
+
+ await preferencesController.addToken(address, symbol, decimals)
+
+ const tokens = preferencesController.getTokens()
+ assert.equal(tokens.length, 1, 'one token added')
+
+ const added = tokens[0]
+ assert.equal(added.address, address, 'set address correctly')
+ assert.equal(added.symbol, symbol, 'set symbol correctly')
+ assert.equal(added.decimals, decimals, 'set decimals correctly')
+ })
+
+ it('should allow updating a token value', async function () {
+ const address = '0xabcdef1234567'
+ const symbol = 'ABBR'
+ const decimals = 5
+
+ await preferencesController.addToken(address, symbol, decimals)
+
+ const newDecimals = 6
+ await preferencesController.addToken(address, symbol, newDecimals)
+
+ const tokens = preferencesController.getTokens()
+ assert.equal(tokens.length, 1, 'one token added')
+
+ const added = tokens[0]
+ assert.equal(added.address, address, 'set address correctly')
+ assert.equal(added.symbol, symbol, 'set symbol correctly')
+ assert.equal(added.decimals, newDecimals, 'updated decimals correctly')
+ })
+ })
+})
+
diff --git a/test/unit/reducers/unlock_vault_test.js b/test/unit/reducers/unlock_vault_test.js
index b7540af08..2b7d70b2c 100644
--- a/test/unit/reducers/unlock_vault_test.js
+++ b/test/unit/reducers/unlock_vault_test.js
@@ -1,32 +1,31 @@
-var jsdom = require('mocha-jsdom')
+// var jsdom = require('mocha-jsdom')
var assert = require('assert')
-var freeze = require('deep-freeze-strict')
+// 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'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-describe('#unlockMetamask(selectedAccount)', function() {
-
- beforeEach(function() {
+describe('#unlockMetamask(selectedAccount)', function () {
+ beforeEach(function () {
// sinon allows stubbing methods that are easily verified
this.sinon = sinon.sandbox.create()
})
- afterEach(function() {
+ afterEach(function () {
// sinon requires cleanup otherwise it will overwrite context
this.sinon.restore()
})
- describe('after an error', function() {
- it('clears warning', function() {
+ describe('after an error', function () {
+ it('clears warning', function () {
const warning = 'this is the wrong warning'
const account = 'foo_account'
const initialState = {
appState: {
warning: warning,
- }
+ },
}
const resultState = reducers(initialState, actions.unlockMetamask(account))
@@ -34,14 +33,14 @@ describe('#unlockMetamask(selectedAccount)', function() {
})
})
- describe('going home after an error', function() {
- it('clears warning', function() {
+ describe('going home after an error', function () {
+ it('clears warning', function () {
const warning = 'this is the wrong warning'
- const account = 'foo_account'
+ // const account = 'foo_account'
const initialState = {
appState: {
warning: warning,
- }
+ },
}
const resultState = reducers(initialState, actions.goHome())
diff --git a/test/unit/responsive/components/dropdown-test.js b/test/unit/responsive/components/dropdown-test.js
new file mode 100644
index 000000000..982d8c6ec
--- /dev/null
+++ b/test/unit/responsive/components/dropdown-test.js
@@ -0,0 +1,81 @@
+const assert = require('assert');
+
+const h = require('react-hyperscript');
+const sinon = require('sinon');
+const path = require('path');
+const Dropdown = require(path.join(__dirname, '..', '..', '..', '..', 'ui', 'app', 'components', 'dropdowns', 'index.js')).Dropdown;
+
+const { createMockStore } = require('redux-test-utils')
+const { mountWithStore } = require('../../../lib/shallow-with-store')
+
+const mockState = {
+ metamask: {
+ }
+}
+
+describe('Dropdown components', function () {
+ let onClickOutside;
+ let closeMenu;
+ let onClick;
+
+ let dropdownComponentProps = {
+ isOpen: true,
+ zIndex: 11,
+ onClickOutside,
+ style: {
+ position: 'absolute',
+ right: 0,
+ top: '36px',
+ },
+ innerStyle: {},
+ }
+
+ let dropdownComponent
+ let store
+ let component
+ beforeEach(function () {
+ onClickOutside = sinon.spy();
+ closeMenu = sinon.spy();
+ onClick = sinon.spy();
+
+ store = createMockStore(mockState)
+ component = mountWithStore(h(
+ Dropdown,
+ dropdownComponentProps,
+ [
+ h('style', `
+ .drop-menu-item:hover { background:rgb(235, 235, 235); }
+ .drop-menu-item i { margin: 11px; }
+ `),
+ h('li', {
+ closeMenu,
+ onClick,
+ }, 'Item 1'),
+ h('li', {
+ closeMenu,
+ onClick,
+ }, 'Item 2'),
+ ]
+ ), store)
+ dropdownComponent = component
+ })
+
+ it('can render two items', function () {
+ const items = dropdownComponent.find('li');
+ assert.equal(items.length, 2);
+ });
+
+ it('closes when item clicked', function() {
+ const items = dropdownComponent.find('li');
+ const node = items.at(0);
+ node.simulate('click');
+ assert.equal(node.props().closeMenu, closeMenu);
+ });
+
+ it('invokes click handler when item clicked', function() {
+ const items = dropdownComponent.find('li');
+ const node = items.at(0);
+ node.simulate('click');
+ assert.equal(onClick.calledOnce, true);
+ });
+});
diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js
new file mode 100644
index 000000000..cc99afee4
--- /dev/null
+++ b/test/unit/tx-controller-test.js
@@ -0,0 +1,413 @@
+const assert = require('assert')
+const ethUtil = require('ethereumjs-util')
+const EthTx = require('ethereumjs-tx')
+const EthjsQuery = require('ethjs-query')
+const ObservableStore = require('obs-store')
+const sinon = require('sinon')
+const TransactionController = require('../../app/scripts/controllers/transactions')
+const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
+const { createTestProviderTools } = require('../stub/provider')
+
+const noop = () => true
+const currentNetworkId = 42
+const otherNetworkId = 36
+const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex')
+
+
+describe('Transaction Controller', function () {
+ let txController, provider, providerResultStub, testBlockchain
+
+ beforeEach(function () {
+ providerResultStub = {
+ // 1 gwei
+ eth_gasPrice: '0x0de0b6b3a7640000',
+ // by default, all accounts are external accounts (not contracts)
+ eth_getCode: '0x',
+ }
+ const providerTools = createTestProviderTools({ scaffold: providerResultStub })
+ provider = providerTools.provider
+ testBlockchain = providerTools.testBlockchain
+
+ txController = new TransactionController({
+ provider,
+ networkStore: new ObservableStore(currentNetworkId),
+ txHistoryLimit: 10,
+ blockTracker: { getCurrentBlock: noop, on: noop, once: noop },
+ signTransaction: (ethTx) => new Promise((resolve) => {
+ ethTx.sign(privKey)
+ resolve()
+ }),
+ })
+ txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop })
+ })
+
+ describe('#getState', function () {
+ it('should return a state object with the right keys and datat types', function () {
+ const exposedState = txController.getState()
+ assert('unapprovedTxs' in exposedState, 'state should have the key unapprovedTxs')
+ assert('selectedAddressTxList' in exposedState, 'state should have the key selectedAddressTxList')
+ assert(typeof exposedState.unapprovedTxs === 'object', 'should be an object')
+ assert(Array.isArray(exposedState.selectedAddressTxList), 'should be an array')
+ })
+ })
+
+ describe('#getUnapprovedTxCount', function () {
+ it('should return the number of unapproved txs', function () {
+ txController.txStateManager._saveTxList([
+ { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 2, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
+ ])
+ const unapprovedTxCount = txController.getUnapprovedTxCount()
+ assert.equal(unapprovedTxCount, 3, 'should be 3')
+ })
+ })
+
+ describe('#getPendingTxCount', function () {
+ it('should return the number of pending txs', function () {
+ txController.txStateManager._saveTxList([
+ { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
+ ])
+ const pendingTxCount = txController.getPendingTxCount()
+ assert.equal(pendingTxCount, 3, 'should be 3')
+ })
+ })
+
+ describe('#getConfirmedTransactions', function () {
+ let address
+ beforeEach(function () {
+ address = '0xc684832530fcbddae4b4230a47e991ddcec2831d'
+ const txParams = {
+ 'from': address,
+ 'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ }
+ txController.txStateManager._saveTxList([
+ {id: 0, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 2, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 3, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 4, status: 'rejected', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 5, status: 'approved', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 6, status: 'signed', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 7, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams},
+ {id: 8, status: 'failed', metamaskNetworkId: currentNetworkId, txParams},
+ ])
+ })
+
+ it('should return the number of confirmed txs', function () {
+ assert.equal(txController.nonceTracker.getConfirmedTransactions(address).length, 3)
+ })
+ })
+
+
+ describe('#newUnapprovedTransaction', function () {
+ let stub, txMeta, txParams
+ beforeEach(function () {
+ txParams = {
+ 'from': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ 'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ }
+ txMeta = {
+ status: 'unapproved',
+ id: 1,
+ metamaskNetworkId: currentNetworkId,
+ txParams,
+ history: [],
+ }
+ txController.txStateManager._saveTxList([txMeta])
+ stub = sinon.stub(txController, 'addUnapprovedTransaction').callsFake(() => {
+ txController.emit('newUnapprovedTx', txMeta)
+ return Promise.resolve(txController.txStateManager.addTx(txMeta))
+ })
+
+ afterEach(function () {
+ txController.txStateManager._saveTxList([])
+ stub.restore()
+ })
+ })
+
+ it('should resolve when finished and status is submitted and resolve with the hash', function (done) {
+ txController.once('newUnapprovedTx', (txMetaFromEmit) => {
+ setTimeout(() => {
+ txController.setTxHash(txMetaFromEmit.id, '0x0')
+ txController.txStateManager.setTxStatusSubmitted(txMetaFromEmit.id)
+ }, 10)
+ })
+
+ txController.newUnapprovedTransaction(txParams)
+ .then((hash) => {
+ assert(hash, 'newUnapprovedTransaction needs to return the hash')
+ done()
+ })
+ .catch(done)
+ })
+
+ it('should reject when finished and status is rejected', function (done) {
+ txController.once('newUnapprovedTx', (txMetaFromEmit) => {
+ setTimeout(() => {
+ txController.txStateManager.setTxStatusRejected(txMetaFromEmit.id)
+ }, 10)
+ })
+
+ txController.newUnapprovedTransaction(txParams)
+ .catch((err) => {
+ if (err.message === 'MetaMask Tx Signature: User denied transaction signature.') done()
+ else done(err)
+ })
+ })
+ })
+
+ describe('#addUnapprovedTransaction', function () {
+
+ it('should add an unapproved transaction and return a valid txMeta', function (done) {
+ txController.addUnapprovedTransaction({})
+ .then((txMeta) => {
+ assert(('id' in txMeta), 'should have a id')
+ assert(('time' in txMeta), 'should have a time stamp')
+ assert(('metamaskNetworkId' in txMeta), 'should have a metamaskNetworkId')
+ assert(('txParams' in txMeta), 'should have a txParams')
+ assert(('history' in txMeta), 'should have a history')
+
+ const memTxMeta = txController.txStateManager.getTx(txMeta.id)
+ assert.deepEqual(txMeta, memTxMeta, `txMeta should be stored in txController after adding it\n expected: ${txMeta} \n got: ${memTxMeta}`)
+ done()
+ }).catch(done)
+ })
+
+ it('should emit newUnapprovedTx event and pass txMeta as the first argument', function (done) {
+ providerResultStub.eth_gasPrice = '4a817c800'
+ txController.once('newUnapprovedTx', (txMetaFromEmit) => {
+ assert(txMetaFromEmit, 'txMeta is falsey')
+ done()
+ })
+ txController.addUnapprovedTransaction({})
+ .catch(done)
+ })
+
+ })
+
+ describe('#addTxDefaults', function () {
+ it('should add the tx defaults if their are none', function (done) {
+ const txMeta = {
+ 'txParams': {
+ 'from': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ 'to': '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ },
+ }
+ providerResultStub.eth_gasPrice = '4a817c800'
+ providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' }
+ providerResultStub.eth_estimateGas = '5209'
+ txController.addTxDefaults(txMeta)
+ .then((txMetaWithDefaults) => {
+ assert(txMetaWithDefaults.txParams.value, '0x0', 'should have added 0x0 as the value')
+ assert(txMetaWithDefaults.txParams.gasPrice, 'should have added the gas price')
+ assert(txMetaWithDefaults.txParams.gas, 'should have added the gas field')
+ done()
+ })
+ .catch(done)
+ })
+ })
+
+ describe('#validateTxParams', function () {
+ it('does not throw for positive values', function (done) {
+ var sample = {
+ value: '0x01',
+ }
+ txController.txGasUtil.validateTxParams(sample).then(() => {
+ done()
+ }).catch(done)
+ })
+
+ it('returns error for negative values', function (done) {
+ var sample = {
+ value: '-0x01',
+ }
+ txController.txGasUtil.validateTxParams(sample)
+ .then(() => done('expected to thrown on negativity values but didn\'t'))
+ .catch((err) => {
+ assert.ok(err, 'error')
+ done()
+ })
+ })
+ })
+
+ describe('#addTx', function () {
+ it('should emit updates', function (done) {
+ const txMeta = {
+ id: '1',
+ status: 'unapproved',
+ metamaskNetworkId: currentNetworkId,
+ txParams: {},
+ }
+
+ const eventNames = ['update:badge', '1:unapproved']
+ const listeners = []
+ eventNames.forEach((eventName) => {
+ listeners.push(new Promise((resolve) => {
+ txController.once(eventName, (arg) => {
+ resolve(arg)
+ })
+ }))
+ })
+ Promise.all(listeners)
+ .then((returnValues) => {
+ assert.deepEqual(returnValues.pop(), txMeta, 'last event 1:unapproved should return txMeta')
+ done()
+ })
+ .catch(done)
+ txController.addTx(txMeta)
+ })
+ })
+
+ describe('#approveTransaction', function () {
+ let txMeta, originalValue
+
+ beforeEach(function () {
+ originalValue = '0x01'
+ txMeta = {
+ id: '1',
+ status: 'unapproved',
+ metamaskNetworkId: currentNetworkId,
+ txParams: {
+ nonce: originalValue,
+ gas: originalValue,
+ gasPrice: originalValue,
+ },
+ }
+ })
+
+
+ it('does not overwrite set values', function (done) {
+ this.timeout(15000)
+ const wrongValue = '0x05'
+
+ txController.addTx(txMeta)
+ providerResultStub.eth_gasPrice = wrongValue
+ providerResultStub.eth_estimateGas = '0x5209'
+
+ const signStub = sinon.stub(txController, 'signTransaction').callsFake(() => Promise.resolve())
+
+ const pubStub = sinon.stub(txController, 'publishTransaction').callsFake(() => {
+ txController.setTxHash('1', originalValue)
+ txController.txStateManager.setTxStatusSubmitted('1')
+ })
+
+ txController.approveTransaction(txMeta.id).then(() => {
+ const result = txController.txStateManager.getTx(txMeta.id)
+ const params = result.txParams
+
+ assert.equal(params.gas, originalValue, 'gas unmodified')
+ assert.equal(params.gasPrice, originalValue, 'gas price unmodified')
+ assert.equal(result.hash, originalValue, `hash was set \n got: ${result.hash} \n expected: ${originalValue}`)
+ signStub.restore()
+ pubStub.restore()
+ done()
+ }).catch(done)
+ })
+ })
+
+ describe('#sign replay-protected tx', function () {
+ it('prepares a tx with the chainId set', function (done) {
+ txController.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txController.signTransaction('1').then((rawTx) => {
+ const ethTx = new EthTx(ethUtil.toBuffer(rawTx))
+ assert.equal(ethTx.getChainId(), currentNetworkId)
+ done()
+ }).catch(done)
+ })
+ })
+
+ describe('#updateAndApproveTransaction', function () {
+ let txMeta
+ beforeEach(function () {
+ txMeta = {
+ id: 1,
+ status: 'unapproved',
+ txParams: {
+ from: '0xc684832530fcbddae4b4230a47e991ddcec2831d',
+ to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ gasPrice: '0x77359400',
+ gas: '0x7b0d',
+ nonce: '0x4b',
+ },
+ metamaskNetworkId: currentNetworkId,
+ }
+ })
+ it('should update and approve transactions', function () {
+ txController.txStateManager.addTx(txMeta)
+ txController.updateAndApproveTransaction(txMeta)
+ const tx = txController.txStateManager.getTx(1)
+ assert.equal(tx.status, 'approved')
+ })
+ })
+
+ describe('#getChainId', function () {
+ it('returns 0 when the chainId is NaN', function () {
+ txController.networkStore = new ObservableStore(NaN)
+ assert.equal(txController.getChainId(), 0)
+ })
+ })
+
+ describe('#cancelTransaction', function () {
+ beforeEach(function () {
+ txController.txStateManager._saveTxList([
+ { id: 0, status: 'unapproved', txParams: {}, metamaskNetworkId: currentNetworkId, history: [{}] },
+ { id: 1, status: 'rejected', txParams: {}, metamaskNetworkId: currentNetworkId, history: [{}] },
+ { id: 2, status: 'approved', txParams: {}, metamaskNetworkId: currentNetworkId, history: [{}] },
+ { id: 3, status: 'signed', txParams: {}, metamaskNetworkId: currentNetworkId, history: [{}] },
+ { id: 4, status: 'submitted', txParams: {}, metamaskNetworkId: currentNetworkId, history: [{}] },
+ { id: 5, status: 'confirmed', txParams: {}, metamaskNetworkId: currentNetworkId, history: [{}] },
+ { id: 6, status: 'failed', txParams: {}, metamaskNetworkId: currentNetworkId, history: [{}] },
+ ])
+ })
+
+ it('should set the transaction to rejected from unapproved', async function () {
+ await txController.cancelTransaction(0)
+ assert.equal(txController.txStateManager.getTx(0).status, 'rejected')
+ })
+
+ })
+
+ describe('#publishTransaction', function () {
+ let hash, txMeta
+ beforeEach(function () {
+ hash = '0x2a5523c6fa98b47b7d9b6c8320179785150b42a16bcff36b398c5062b65657e8'
+ txMeta = {
+ id: 1,
+ status: 'unapproved',
+ txParams: {},
+ metamaskNetworkId: currentNetworkId,
+ }
+ providerResultStub.eth_sendRawTransaction = hash
+ })
+
+ it('should publish a tx, updates the rawTx when provided a one', async function () {
+ txController.txStateManager.addTx(txMeta)
+ await txController.publishTransaction(txMeta.id)
+ const publishedTx = txController.txStateManager.getTx(1)
+ assert.equal(publishedTx.hash, hash)
+ assert.equal(publishedTx.status, 'submitted')
+ })
+ })
+
+
+ describe('#getPendingTransactions', function () {
+ beforeEach(function () {
+ txController.txStateManager._saveTxList([
+ { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 2, status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 3, status: 'approved', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 4, status: 'signed', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 5, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 6, status: 'confimed', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 7, status: 'failed', metamaskNetworkId: currentNetworkId, txParams: {} },
+ ])
+ })
+ it('should show only submitted transactions as pending transasction', function () {
+ assert(txController.pendingTxTracker.getPendingTransactions().length, 1)
+ assert(txController.pendingTxTracker.getPendingTransactions()[0].status, 'submitted')
+ })
+ })
+})
diff --git a/test/unit/tx-gas-util-test.js b/test/unit/tx-gas-util-test.js
new file mode 100644
index 000000000..d9a12d1c3
--- /dev/null
+++ b/test/unit/tx-gas-util-test.js
@@ -0,0 +1,32 @@
+const assert = require('assert')
+const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils')
+const { createTestProviderTools } = require('../stub/provider')
+
+describe('Tx Gas Util', function () {
+ let txGasUtil, provider, providerResultStub
+ beforeEach(function () {
+ providerResultStub = {}
+ provider = createTestProviderTools({ scaffold: providerResultStub }).provider
+ txGasUtil = new TxGasUtils({
+ provider,
+ })
+ })
+
+ it('removes recipient for txParams with 0x when contract data is provided', function () {
+ const zeroRecipientandDataTxParams = {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ to: '0x',
+ data: 'bytecode',
+ }
+ const sanitizedTxParams = txGasUtil.validateRecipient(zeroRecipientandDataTxParams)
+ assert.deepEqual(sanitizedTxParams, { from: '0x1678a085c290ebd122dc42cba69373b5953b831d', data: 'bytecode' }, 'no recipient with 0x')
+ })
+
+ it('should error when recipient is 0x', function () {
+ const zeroRecipientTxParams = {
+ from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
+ to: '0x',
+ }
+ assert.throws(() => { txGasUtil.validateRecipient(zeroRecipientTxParams) }, Error, 'Invalid recipient address')
+ })
+})
diff --git a/test/unit/tx-helper-test.js b/test/unit/tx-helper-test.js
new file mode 100644
index 000000000..cc6543c30
--- /dev/null
+++ b/test/unit/tx-helper-test.js
@@ -0,0 +1,17 @@
+const assert = require('assert')
+const txHelper = require('../../ui/lib/tx-helper')
+
+describe('txHelper', function () {
+ it('always shows the oldest tx first', function () {
+ const metamaskNetworkId = 1
+ const txs = {
+ a: { metamaskNetworkId, time: 3 },
+ b: { metamaskNetworkId, time: 1 },
+ c: { metamaskNetworkId, time: 2 },
+ }
+
+ const sorted = txHelper(txs, null, null, metamaskNetworkId)
+ assert.equal(sorted[0].time, 1, 'oldest tx first')
+ assert.equal(sorted[2].time, 3, 'newest tx last')
+ })
+})
diff --git a/test/unit/tx-manager-test.js b/test/unit/tx-manager-test.js
deleted file mode 100644
index 21e94357b..000000000
--- a/test/unit/tx-manager-test.js
+++ /dev/null
@@ -1,241 +0,0 @@
-const assert = require('assert')
-const extend = require('xtend')
-const EventEmitter = require('events')
-const ethUtil = require('ethereumjs-util')
-const EthTx = require('ethereumjs-tx')
-const ObservableStore = require('obs-store')
-const STORAGE_KEY = 'metamask-persistance-key'
-const TransactionManager = require('../../app/scripts/transaction-manager')
-const noop = () => true
-const currentNetworkId = 42
-const otherNetworkId = 36
-const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f33003270ec03e', 'hex')
-
-describe('Transaction Manager', function() {
- let txManager
-
- beforeEach(function() {
- txManager = new TransactionManager({
- networkStore: new ObservableStore({ network: currentNetworkId }),
- txHistoryLimit: 10,
- blockTracker: new EventEmitter(),
- signTransaction: (ethTx) => new Promise((resolve) => {
- ethTx.sign(privKey)
- resolve()
- })
- })
- })
-
- 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: currentNetworkId, 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('does not override txs from other networks', function() {
- var tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
- var tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
- txManager.addTx(tx, noop)
- txManager.addTx(tx2, noop)
- var result = txManager.getFullTxList()
- var result2 = txManager.getTxList()
- assert.equal(result.length, 2, 'txs were deleted')
- assert.equal(result2.length, 1, 'incorrect number of txs on network.')
- })
-
- 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: currentNetworkId, 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: currentNetworkId, 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: currentNetworkId, 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: currentNetworkId, 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: currentNetworkId, 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: currentNetworkId, 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: currentNetworkId, 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: currentNetworkId, 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: currentNetworkId, txParams: {} }, noop)
- txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- txManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: currentNetworkId, 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: currentNetworkId, txParams: {} }, noop)
- txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, 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: currentNetworkId, txParams: {} }, noop)
- txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, 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: currentNetworkId },
- { id: 1, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 2, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 3, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 4, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 5, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 6, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
- { id: 7, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- { id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
- ]
- 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)}`)
- })
- })
-
- describe('#sign replay-protected tx', function() {
- it('prepares a tx with the chainId set', function() {
- txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
- txManager.signTransaction('1', (err, rawTx) => {
- if (err) return assert.fail('it should not fail')
- const ethTx = new EthTx(ethUtil.toBuffer(rawTx))
- assert.equal(ethTx.getChainId(), currentNetworkId)
- })
- })
- })
-
-})
diff --git a/test/unit/tx-state-history-helper-test.js b/test/unit/tx-state-history-helper-test.js
new file mode 100644
index 000000000..90cb10713
--- /dev/null
+++ b/test/unit/tx-state-history-helper-test.js
@@ -0,0 +1,26 @@
+const assert = require('assert')
+const clone = require('clone')
+const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper')
+
+describe('deepCloneFromTxMeta', function () {
+ it('should clone deep', function () {
+ const input = {
+ foo: {
+ bar: {
+ bam: 'baz'
+ }
+ }
+ }
+ const output = txStateHistoryHelper.snapshotFromTxMeta(input)
+ assert('foo' in output, 'has a foo key')
+ assert('bar' in output.foo, 'has a bar key')
+ assert('bam' in output.foo.bar, 'has a bar key')
+ assert.equal(output.foo.bar.bam, 'baz', 'has a baz value')
+ })
+
+ it('should remove the history key', function () {
+ const input = { foo: 'bar', history: 'remembered' }
+ const output = txStateHistoryHelper.snapshotFromTxMeta(input)
+ assert(typeof output.history, 'undefined', 'should remove history')
+ })
+})
diff --git a/test/unit/tx-state-history-helper.js b/test/unit/tx-state-history-helper.js
new file mode 100644
index 000000000..79ee26d6e
--- /dev/null
+++ b/test/unit/tx-state-history-helper.js
@@ -0,0 +1,46 @@
+const assert = require('assert')
+const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper')
+const testVault = require('../data/v17-long-history.json')
+
+
+describe('tx-state-history-helper', function () {
+ it('migrates history to diffs and can recover original values', function () {
+ testVault.data.TransactionController.transactions.forEach((tx, index) => {
+ const newHistory = txStateHistoryHelper.migrateFromSnapshotsToDiffs(tx.history)
+ newHistory.forEach((newEntry, index) => {
+ if (index === 0) {
+ assert.equal(Array.isArray(newEntry), false, 'initial history item IS NOT a json patch obj')
+ } else {
+ assert.equal(Array.isArray(newEntry), true, 'non-initial history entry IS a json patch obj')
+ }
+ const oldEntry = tx.history[index]
+ const historySubset = newHistory.slice(0, index + 1)
+ const reconstructedValue = txStateHistoryHelper.replayHistory(historySubset)
+ assert.deepEqual(oldEntry, reconstructedValue, 'was able to reconstruct old entry from diffs')
+ })
+ })
+ })
+
+ it('replaying history does not mutate the original obj', function () {
+ const initialState = { test: true, message: 'hello', value: 1 }
+ const diff1 = [{
+ "op": "replace",
+ "path": "/message",
+ "value": "haay",
+ }]
+ const diff2 = [{
+ "op": "replace",
+ "path": "/value",
+ "value": 2,
+ }]
+ const history = [initialState, diff1, diff2]
+
+ const beforeStateSnapshot = JSON.stringify(initialState)
+ const latestState = txStateHistoryHelper.replayHistory(history)
+ const afterStateSnapshot = JSON.stringify(initialState)
+
+ assert.notEqual(initialState, latestState, 'initial state is not the same obj as the latest state')
+ assert.equal(beforeStateSnapshot, afterStateSnapshot, 'initial state is not modified during run')
+ })
+
+})
diff --git a/test/unit/tx-state-manager-test.js b/test/unit/tx-state-manager-test.js
new file mode 100644
index 000000000..02dc52967
--- /dev/null
+++ b/test/unit/tx-state-manager-test.js
@@ -0,0 +1,284 @@
+const assert = require('assert')
+const clone = require('clone')
+const ObservableStore = require('obs-store')
+const TxStateManager = require('../../app/scripts/lib/tx-state-manager')
+const txStateHistoryHelper = require('../../app/scripts/lib/tx-state-history-helper')
+const noop = () => true
+
+describe('TransactionStateManger', function () {
+ let txStateManager
+ const currentNetworkId = 42
+ const otherNetworkId = 2
+
+ beforeEach(function () {
+ txStateManager = new TxStateManager({
+ initState: {
+ transactions: [],
+ },
+ txHistoryLimit: 10,
+ getNetwork: () => currentNetworkId
+ })
+ })
+
+ describe('#setTxStatusSigned', function () {
+ it('sets the tx status to signed', function () {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ txStateManager.setTxStatusSigned(1)
+ let result = txStateManager.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) => {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ const noop = function () {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txStateManager.addTx(tx)
+ txStateManager.on('1:signed', noop)
+ txStateManager.setTxStatusSigned(1)
+
+ })
+ })
+
+ describe('#setTxStatusRejected', function () {
+ it('sets the tx status to rejected', function () {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx)
+ txStateManager.setTxStatusRejected(1)
+ let result = txStateManager.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) => {
+ let tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx)
+ const noop = function (err, txId) {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txStateManager.on('1:rejected', noop)
+ txStateManager.setTxStatusRejected(1)
+ })
+ })
+
+ describe('#getFullTxList', function () {
+ it('when new should return empty array', function () {
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ })
+
+ describe('#getTxList', function () {
+ it('when new should return empty array', function () {
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ })
+
+ describe('#addTx', function () {
+ it('adds a tx returned in getTxList', function () {
+ let tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ let result = txStateManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].id, 1)
+ })
+
+ it('does not override txs from other networks', function () {
+ let tx = { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ let tx2 = { id: 2, status: 'confirmed', metamaskNetworkId: otherNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ txStateManager.addTx(tx2, noop)
+ let result = txStateManager.getFullTxList()
+ let result2 = txStateManager.getTxList()
+ assert.equal(result.length, 2, 'txs were deleted')
+ assert.equal(result2.length, 1, 'incorrect number of txs on network.')
+ })
+
+ it('cuts off early txs beyond a limit', function () {
+ const limit = txStateManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ }
+ let result = txStateManager.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 = txStateManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ const tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ }
+ let result = txStateManager.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 () {
+ let unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(unconfirmedTx, noop)
+ const limit = txStateManager.txHistoryLimit
+ for (let i = 1; i < limit + 1; i++) {
+ const tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }
+ txStateManager.addTx(tx, noop)
+ }
+ let result = txStateManager.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('#updateTx', function () {
+ it('replaces the tx with the same id', function () {
+ txStateManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txStateManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ const txMeta = txStateManager.getTx('1')
+ txMeta.hash = 'foo'
+ txStateManager.updateTx(txMeta)
+ let result = txStateManager.getTx('1')
+ assert.equal(result.hash, 'foo')
+ })
+
+ it('updates gas price and adds history items', function () {
+ const originalGasPrice = '0x01'
+ const desiredGasPrice = '0x02'
+
+ const txMeta = {
+ id: '1',
+ status: 'unapproved',
+ metamaskNetworkId: currentNetworkId,
+ txParams: {
+ gasPrice: originalGasPrice,
+ },
+ }
+
+ const updatedMeta = clone(txMeta)
+
+ txStateManager.addTx(txMeta)
+ const updatedTx = txStateManager.getTx('1')
+ // verify tx was initialized correctly
+ assert.equal(updatedTx.history.length, 1, 'one history item (initial)')
+ assert.equal(Array.isArray(updatedTx.history[0]), false, 'first history item is initial state')
+ assert.deepEqual(updatedTx.history[0], txStateHistoryHelper.snapshotFromTxMeta(updatedTx), 'first history item is initial state')
+ // modify value and updateTx
+ updatedTx.txParams.gasPrice = desiredGasPrice
+ txStateManager.updateTx(updatedTx)
+ // check updated value
+ const result = txStateManager.getTx('1')
+ assert.equal(result.txParams.gasPrice, desiredGasPrice, 'gas price updated')
+ // validate history was updated
+ assert.equal(result.history.length, 2, 'two history items (initial + diff)')
+ const expectedEntry = { op: 'replace', path: '/txParams/gasPrice', value: desiredGasPrice }
+ assert.deepEqual(result.history[1], [expectedEntry], 'two history items (initial + diff)')
+ })
+ })
+
+ describe('#getUnapprovedTxList', function () {
+ it('returns unapproved txs in a hash', function () {
+ txStateManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txStateManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ const result = txStateManager.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 () {
+ txStateManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ txStateManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} }, noop)
+ assert.equal(txStateManager.getTx('1').status, 'unapproved')
+ assert.equal(txStateManager.getTx('2').status, 'confirmed')
+ })
+ })
+
+ describe('#getFilteredTxList', function () {
+ it('returns a tx with the requested data', function () {
+ const txMetas = [
+ { id: 0, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 1, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 2, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 3, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 4, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 5, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 6, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: currentNetworkId },
+ { id: 7, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ { id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: currentNetworkId },
+ ]
+ txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
+ let filterParams
+
+ filterParams = { status: 'unapproved', from: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'unapproved', to: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 2, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed', from: '0xbb' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { from: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { to: '0xaa' }
+ assert.equal(txStateManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ })
+ })
+
+ describe('#wipeTransactions', function () {
+
+ const specificAddress = '0xaa'
+ const otherAddress = '0xbb'
+
+ it('should remove only the transactions from a specific address', function () {
+
+ const txMetas = [
+ { id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId },
+ { id: 1, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId },
+ { id: 2, status: 'confirmed', txParams: { from: otherAddress, to: specificAddress }, metamaskNetworkId: currentNetworkId },
+ ]
+ txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
+
+ txStateManager.wipeTransactions(specificAddress)
+
+ const transactionsFromCurrentAddress = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from === specificAddress)
+ const transactionsFromOtherAddresses = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from !== specificAddress)
+
+ assert.equal(transactionsFromCurrentAddress.length, 0)
+ assert.equal(transactionsFromOtherAddresses.length, 2)
+ })
+
+ it('should not remove the transactions from other networks', function () {
+ const txMetas = [
+ { id: 0, status: 'unapproved', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: currentNetworkId },
+ { id: 1, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
+ { id: 2, status: 'confirmed', txParams: { from: specificAddress, to: otherAddress }, metamaskNetworkId: otherNetworkId },
+ ]
+
+ txMetas.forEach((txMeta) => txStateManager.addTx(txMeta, noop))
+
+ txStateManager.wipeTransactions(specificAddress)
+
+ const txsFromCurrentNetworkAndAddress = txStateManager.getTxList().filter((txMeta) => txMeta.txParams.from === specificAddress)
+ const txFromOtherNetworks = txStateManager.getFullTxList().filter((txMeta) => txMeta.metamaskNetworkId === otherNetworkId)
+
+ assert.equal(txsFromCurrentNetworkAndAddress.length, 0)
+ assert.equal(txFromOtherNetworks.length, 2)
+
+ })
+ })
+}) \ No newline at end of file
diff --git a/test/unit/tx-utils-test.js b/test/unit/tx-utils-test.js
index 93e9e4134..8ca13412e 100644
--- a/test/unit/tx-utils-test.js
+++ b/test/unit/tx-utils-test.js
@@ -1,19 +1,25 @@
const assert = require('assert')
-const ethUtil = require('ethereumjs-util')
-const BN = ethUtil.BN
+const Transaction = require('ethereumjs-tx')
+const BN = require('bn.js')
-const TxUtils = require('../../app/scripts/lib/tx-utils')
+const { hexToBn, bnToHex } = require('../../app/scripts/lib/util')
+const TxUtils = require('../../app/scripts/lib/tx-gas-utils')
-describe('txUtils', function() {
+
+describe('txUtils', function () {
let txUtils
- before(function() {
- txUtils = new TxUtils()
+ before(function () {
+ txUtils = new TxUtils(new Proxy({}, {
+ get: (obj, name) => {
+ return () => {}
+ },
+ }))
})
- describe('chain Id', function() {
- it('prepares a transaction with the provided chainId', function() {
+ describe('chain Id', function () {
+ it('prepares a transaction with the provided chainId', function () {
const txParams = {
to: '0x70ad465e0bab6504002ad58c744ed89c7da38524',
from: '0x69ad465e0bab6504002ad58c744ed89c7da38525',
@@ -24,13 +30,13 @@ describe('txUtils', function() {
nonce: '0x3',
chainId: 42,
}
- const ethTx = txUtils.buildEthTxFromParams(txParams)
+ const ethTx = new Transaction(txParams)
assert.equal(ethTx.getChainId(), 42, 'chainId is set from tx params')
})
})
- describe('addGasBuffer', function() {
- it('multiplies by 1.5, when within block gas limit', function() {
+ describe('addGasBuffer', function () {
+ it('multiplies by 1.5, when within block gas limit', function () {
// naive estimatedGas: 0x16e360 (1.5 mil)
const inputHex = '0x16e360'
// dummy gas limit: 0x3d4c52 (4 mil)
@@ -41,20 +47,20 @@ describe('txUtils', function() {
const expectedBn = inputBn.muln(1.5)
assert(outputBn.eq(expectedBn), 'returns 1.5 the input value')
})
-
- it('uses original estimatedGas, when above block gas limit', function() {
+
+ it('uses original estimatedGas, when above block gas limit', function () {
// naive estimatedGas: 0x16e360 (1.5 mil)
const inputHex = '0x16e360'
// dummy gas limit: 0x0f4240 (1 mil)
const blockGasLimitHex = '0x0f4240'
const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex)
- const inputBn = hexToBn(inputHex)
+ // const inputBn = hexToBn(inputHex)
const outputBn = hexToBn(output)
const expectedBn = hexToBn(inputHex)
assert(outputBn.eq(expectedBn), 'returns the original estimatedGas value')
})
- it('buffers up to reccomend gas limit reccomended ceiling', function() {
+ it('buffers up to recommend gas limit recommended ceiling', function () {
// naive estimatedGas: 0x16e360 (1.5 mil)
const inputHex = '0x16e360'
// dummy gas limit: 0x1e8480 (2 mil)
@@ -65,17 +71,7 @@ describe('txUtils', function() {
// const inputBn = hexToBn(inputHex)
// const outputBn = hexToBn(output)
const expectedHex = bnToHex(ceilGasLimitBn)
- assert.equal(output, expectedHex, 'returns the gas limit reccomended ceiling value')
+ assert.equal(output, expectedHex, 'returns the gas limit recommended ceiling value')
})
})
-})
-
-// util
-
-function hexToBn(inputHex) {
- return new BN(ethUtil.stripHexPrefix(inputHex), 16)
-}
-
-function bnToHex(inputBn) {
- return ethUtil.addHexPrefix(inputBn.toString(16))
-} \ No newline at end of file
+}) \ No newline at end of file
diff --git a/test/unit/ui/add-token.spec.js b/test/unit/ui/add-token.spec.js
new file mode 100644
index 000000000..69b7fb620
--- /dev/null
+++ b/test/unit/ui/add-token.spec.js
@@ -0,0 +1,43 @@
+const assert = require('assert')
+const { createMockStore } = require('redux-test-utils')
+const h = require('react-hyperscript')
+const { shallowWithStore } = require('../../lib/shallow-with-store')
+const AddTokenScreen = require('../../../old-ui/app/add-token')
+
+describe('Add Token Screen', function () {
+ let addTokenComponent, store, component
+ const mockState = {
+ metamask: {
+ identities: {
+ '0x7d3517b0d011698406d6e0aed8453f0be2697926': {
+ 'address': '0x7d3517b0d011698406d6e0aed8453f0be2697926',
+ 'name': 'Add Token Name',
+ },
+ },
+ },
+ }
+ beforeEach(function () {
+ store = createMockStore(mockState)
+ component = shallowWithStore(h(AddTokenScreen), store)
+ addTokenComponent = component.dive()
+ })
+
+ describe('#ValidateInputs', function () {
+
+ it('Default State', function () {
+ addTokenComponent.instance().validateInputs()
+ const state = addTokenComponent.state()
+ assert.equal(state.warning, 'Address is invalid.')
+ })
+
+ it('Address is a Metamask Identity', function () {
+ addTokenComponent.setState({
+ address: '0x7d3517b0d011698406d6e0aed8453f0be2697926',
+ })
+ addTokenComponent.instance().validateInputs()
+ const state = addTokenComponent.state()
+ assert.equal(state.warning, 'Personal address detected. Input the token contract address.')
+ })
+
+ })
+})
diff --git a/test/unit/util-test.js b/test/unit/util-test.js
new file mode 100644
index 000000000..6da185b2c
--- /dev/null
+++ b/test/unit/util-test.js
@@ -0,0 +1,41 @@
+const assert = require('assert')
+const { sufficientBalance } = require('../../app/scripts/lib/util')
+
+
+describe('SufficientBalance', function () {
+ it('returns true if max tx cost is equal to balance.', function () {
+ const tx = {
+ 'value': '0x1',
+ 'gas': '0x2',
+ 'gasPrice': '0x3',
+ }
+ const balance = '0x8'
+
+ const result = sufficientBalance(tx, balance)
+ assert.ok(result, 'sufficient balance found.')
+ })
+
+ it('returns true if max tx cost is less than balance.', function () {
+ const tx = {
+ 'value': '0x1',
+ 'gas': '0x2',
+ 'gasPrice': '0x3',
+ }
+ const balance = '0x9'
+
+ const result = sufficientBalance(tx, balance)
+ assert.ok(result, 'sufficient balance found.')
+ })
+
+ it('returns false if max tx cost is more than balance.', function () {
+ const tx = {
+ 'value': '0x1',
+ 'gas': '0x2',
+ 'gasPrice': '0x3',
+ }
+ const balance = '0x6'
+
+ const result = sufficientBalance(tx, balance)
+ assert.ok(!result, 'insufficient balance found.')
+ })
+}) \ No newline at end of file
diff --git a/test/unit/util_test.js b/test/unit/util_test.js
index 00528b905..59048975a 100644
--- a/test/unit/util_test.js
+++ b/test/unit/util_test.js
@@ -5,96 +5,96 @@ const ethUtil = require('ethereumjs-util')
var path = require('path')
var util = require(path.join(__dirname, '..', '..', 'ui', 'app', 'util.js'))
-describe('util', function() {
+describe('util', function () {
var ethInWei = '1'
- for (var i = 0; i < 18; i++ ) { ethInWei += '0' }
+ for (var i = 0; i < 18; i++) { ethInWei += '0' }
- beforeEach(function() {
+ beforeEach(function () {
this.sinon = sinon.sandbox.create()
})
- afterEach(function() {
+ afterEach(function () {
this.sinon.restore()
})
- describe('#parseBalance', function() {
- it('should render 0.01 eth correctly', function() {
+ describe('#parseBalance', function () {
+ it('should render 0.01 eth correctly', function () {
const input = '0x2386F26FC10000'
const output = util.parseBalance(input)
assert.deepEqual(output, ['0', '01'])
})
- it('should render 12.023 eth correctly', function() {
+ it('should render 12.023 eth correctly', function () {
const input = 'A6DA46CCA6858000'
const output = util.parseBalance(input)
assert.deepEqual(output, ['12', '023'])
})
- it('should render 0.0000000342422 eth correctly', function() {
+ it('should render 0.0000000342422 eth correctly', function () {
const input = '0x7F8FE81C0'
const output = util.parseBalance(input)
assert.deepEqual(output, ['0', '0000000342422'])
})
- it('should render 0 eth correctly', function() {
+ it('should render 0 eth correctly', function () {
const input = '0x0'
const output = util.parseBalance(input)
assert.deepEqual(output, ['0', '0'])
})
})
- describe('#addressSummary', function() {
- it('should add case-sensitive checksum', function() {
+ describe('#addressSummary', function () {
+ it('should add case-sensitive checksum', function () {
var address = '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825'
var result = util.addressSummary(address)
assert.equal(result, '0xFDEa65C8...b825')
})
- it('should accept arguments for firstseg, lastseg, and keepPrefix', function() {
+ it('should accept arguments for firstseg, lastseg, and keepPrefix', function () {
var address = '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825'
var result = util.addressSummary(address, 4, 4, false)
assert.equal(result, 'FDEa...b825')
})
})
- describe('#isValidAddress', function() {
- it('should allow 40-char non-prefixed hex', function() {
+ describe('#isValidAddress', function () {
+ it('should allow 40-char non-prefixed hex', function () {
var address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b825'
var result = util.isValidAddress(address)
assert.ok(result)
})
- it('should allow 42-char non-prefixed hex', function() {
+ it('should allow 42-char non-prefixed hex', function () {
var address = '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825'
var result = util.isValidAddress(address)
assert.ok(result)
})
- it('should not allow less non hex-prefixed', function() {
+ it('should not allow less non hex-prefixed', function () {
var address = 'fdea65c8e26263f6d9a1b5de9555d2931a33b85'
var result = util.isValidAddress(address)
assert.ok(!result)
})
- it('should not allow less hex-prefixed', function() {
+ it('should not allow less hex-prefixed', function () {
var address = '0xfdea65ce26263f6d9a1b5de9555d2931a33b85'
var result = util.isValidAddress(address)
assert.ok(!result)
})
- it('should recognize correct capitalized checksum', function() {
+ it('should recognize correct capitalized checksum', function () {
var address = '0xFDEa65C8e26263F6d9A1B5de9555D2931A33b825'
var result = util.isValidAddress(address)
assert.ok(result)
})
- it('should recognize incorrect capitalized checksum', function() {
+ it('should recognize incorrect capitalized checksum', function () {
var address = '0xFDea65C8e26263F6d9A1B5de9555D2931A33b825'
var result = util.isValidAddress(address)
assert.ok(!result)
})
- it('should recognize this sample hashed address', function() {
+ it('should recognize this sample hashed address', function () {
const address = '0x5Fda30Bb72B8Dfe20e48A00dFc108d0915BE9Bb0'
const result = util.isValidAddress(address)
const hashed = ethUtil.toChecksumAddress(address.toLowerCase())
@@ -103,60 +103,57 @@ describe('util', function() {
})
})
- describe('#numericBalance', function() {
-
- it('should return a BN 0 if given nothing', function() {
+ describe('#numericBalance', function () {
+ it('should return a BN 0 if given nothing', function () {
var result = util.numericBalance()
assert.equal(result.toString(10), 0)
})
- it('should work with hex prefix', function() {
+ it('should work with hex prefix', function () {
var result = util.numericBalance('0x012')
assert.equal(result.toString(10), '18')
})
- it('should work with no hex prefix', function() {
+ it('should work with no hex prefix', function () {
var result = util.numericBalance('012')
assert.equal(result.toString(10), '18')
})
-
})
- describe('#formatBalance', function() {
-
- it('when given nothing', function() {
+ describe('#formatBalance', function () {
+ it('when given nothing', function () {
var result = util.formatBalance()
assert.equal(result, 'None', 'should return "None"')
})
- it('should return eth as string followed by ETH', function() {
+ it('should return eth as string followed by ETH', function () {
var input = new ethUtil.BN(ethInWei, 10).toJSON()
var result = util.formatBalance(input, 4)
assert.equal(result, '1.0000 ETH')
})
- it('should return eth as string followed by ETH', function() {
+ it('should return eth as string followed by ETH', function () {
var input = new ethUtil.BN(ethInWei, 10).div(new ethUtil.BN('2', 10)).toJSON()
var result = util.formatBalance(input, 3)
assert.equal(result, '0.500 ETH')
})
- it('should display specified decimal points', function() {
- var input = "0x128dfa6a90b28000"
+ it('should display specified decimal points', function () {
+ var input = '0x128dfa6a90b28000'
var result = util.formatBalance(input, 2)
assert.equal(result, '1.33 ETH')
})
- it('should default to 3 decimal points', function() {
- var input = "0x128dfa6a90b28000"
+ it('should default to 3 decimal points', function () {
+ var input = '0x128dfa6a90b28000'
var result = util.formatBalance(input)
assert.equal(result, '1.337 ETH')
})
- it('should show 2 significant digits for tiny balances', function() {
- var input = "0x1230fa6a90b28"
+ it('should show 2 significant digits for tiny balances', function () {
+ var input = '0x1230fa6a90b28'
var result = util.formatBalance(input)
assert.equal(result, '0.00032 ETH')
})
- it('should not parse the balance and return value with 2 decimal points with ETH at the end', function() {
+ it('should not parse the balance and return value with 2 decimal points with ETH at the end', function () {
var value = '1.2456789'
var needsParse = false
var result = util.formatBalance(value, 2, needsParse)
@@ -164,17 +161,16 @@ describe('util', function() {
})
})
- describe('normalizing values', function() {
-
- describe('#normalizeToWei', function() {
- it('should convert an eth to the appropriate equivalent values', function() {
+ describe('normalizing values', function () {
+ describe('#normalizeToWei', function () {
+ it('should convert an eth to the appropriate equivalent values', function () {
var valueTable = {
- wei: '1000000000000000000',
- kwei: '1000000000000000',
- mwei: '1000000000000',
- gwei: '1000000000',
+ wei: '1000000000000000000',
+ kwei: '1000000000000000',
+ mwei: '1000000000000',
+ gwei: '1000000000',
szabo: '1000000',
- finney:'1000',
+ finney: '1000',
ether: '1',
// kether:'0.001',
// mether:'0.000001',
@@ -185,8 +181,7 @@ describe('util', function() {
}
var oneEthBn = new ethUtil.BN(ethInWei, 10)
- for(var currency in valueTable) {
-
+ for (var currency in valueTable) {
var value = new ethUtil.BN(valueTable[currency], 10)
var output = util.normalizeToWei(value, currency)
assert.equal(output.toString(10), valueTable.wei, `value of ${output.toString(10)} ${currency} should convert to ${oneEthBn}`)
@@ -194,60 +189,70 @@ describe('util', function() {
})
})
- describe('#normalizeEthStringToWei', function() {
- it('should convert decimal eth to pure wei BN', function() {
+ describe('#normalizeEthStringToWei', function () {
+ it('should convert decimal eth to pure wei BN', function () {
var input = '1.23456789'
var output = util.normalizeEthStringToWei(input)
assert.equal(output.toString(10), '1234567890000000000')
})
- it('should convert 1 to expected wei', function() {
+ it('should convert 1 to expected wei', function () {
var input = '1'
var output = util.normalizeEthStringToWei(input)
assert.equal(output.toString(10), ethInWei)
})
- })
- describe('#normalizeNumberToWei', function() {
+ it('should account for overflow numbers gracefully by dropping extra precision.', function () {
+ var input = '1.11111111111111111111'
+ var output = util.normalizeEthStringToWei(input)
+ assert.equal(output.toString(10), '1111111111111111111')
+ })
+
+ it('should not truncate very exact wei values that do not have extra precision.', function () {
+ var input = '1.100000000000000001'
+ var output = util.normalizeEthStringToWei(input)
+ assert.equal(output.toString(10), '1100000000000000001')
+ })
+ })
- it('should handle a simple use case', function() {
+ describe('#normalizeNumberToWei', function () {
+ it('should handle a simple use case', function () {
var input = 0.0002
var output = util.normalizeNumberToWei(input, 'ether')
var str = output.toString(10)
assert.equal(str, '200000000000000')
})
- it('should convert a kwei number to the appropriate equivalent wei', function() {
+ it('should convert a kwei number to the appropriate equivalent wei', function () {
var result = util.normalizeNumberToWei(1.111, 'kwei')
assert.equal(result.toString(10), '1111', 'accepts decimals')
})
- it('should convert a ether number to the appropriate equivalent wei', function() {
+ it('should convert a ether number to the appropriate equivalent wei', function () {
var result = util.normalizeNumberToWei(1.111, 'ether')
assert.equal(result.toString(10), '1111000000000000000', 'accepts decimals')
})
})
- describe('#isHex', function(){
- it('should return true when given a hex string', function() {
+ 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() {
+ 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() {
+ 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() {
+ it('should return true when given a hex string with hex-prefix', function () {
var result = util.isHex('0xc3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2')
assert(result)
})
-
})
})
})