aboutsummaryrefslogtreecommitdiffstats
path: root/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit')
-rw-r--r--test/unit/actions/config_test.js43
-rw-r--r--test/unit/actions/restore_vault_test.js54
-rw-r--r--test/unit/actions/set_selected_account_test.js28
-rw-r--r--test/unit/actions/tx_test.js168
-rw-r--r--test/unit/actions/view_info_test.js23
-rw-r--r--test/unit/actions/warning_test.js24
-rw-r--r--test/unit/migrations-test.js1
-rw-r--r--test/unit/util_test.js114
8 files changed, 454 insertions, 1 deletions
diff --git a/test/unit/actions/config_test.js b/test/unit/actions/config_test.js
new file mode 100644
index 000000000..6a0d20f31
--- /dev/null
+++ b/test/unit/actions/config_test.js
@@ -0,0 +1,43 @@
+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 initialState = {
+ metamask: {
+ rpcTarget: 'foo',
+ },
+ appState: {
+ currentView: {
+ name: 'accounts',
+ }
+ }
+ }
+ freeze(initialState)
+
+ 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() {
+ const action = {
+ type: actions.SET_RPC_TARGET,
+ value: 'bar',
+ }
+
+ var result = reducers(initialState, action)
+ assert.equal(result.metamask.rpcTarget, action.value)
+ })
+ })
+})
+
diff --git a/test/unit/actions/restore_vault_test.js b/test/unit/actions/restore_vault_test.js
new file mode 100644
index 000000000..5873a0181
--- /dev/null
+++ b/test/unit/actions/restore_vault_test.js
@@ -0,0 +1,54 @@
+var jsdom = require('mocha-jsdom')
+var assert = require('assert')
+var freeze = require('deep-freeze-strict')
+var path = require('path')
+var sinon = require('sinon')
+
+var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
+var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
+
+describe('#recoverFromSeed(password, seed)', function() {
+
+ beforeEach(function() {
+ // sinon allows stubbing methods that are easily verified
+ this.sinon = sinon.sandbox.create()
+ })
+
+ afterEach(function() {
+ // sinon requires cleanup otherwise it will overwrite context
+ this.sinon.restore()
+ })
+
+ // stub out account manager
+ actions._setAccountManager({
+ recoverFromSeed(pw, seed, cb) { cb() },
+ })
+
+ it('sets metamask.isUnlocked to true', function() {
+ var initialState = {
+ metamask: {
+ isUnlocked: false,
+ isInitialized: false,
+ }
+ }
+ freeze(initialState)
+
+ const restorePhrase = 'invite heavy among daring outdoor dice jelly coil stable note seat vicious'
+ const password = 'foo'
+ const dispatchFunc = actions.recoverFromSeed(password, restorePhrase)
+
+ var dispatchStub = this.sinon.stub()
+ dispatchStub.withArgs({ TYPE: actions.unlockMetamask() }).onCall(0)
+ dispatchStub.withArgs({ TYPE: actions.showAccountsPage() }).onCall(1)
+
+ var action
+ var resultingState = initialState
+ dispatchFunc((newAction) => {
+ action = newAction
+ resultingState = reducers(resultingState, action)
+ })
+
+ assert.equal(resultingState.metamask.isUnlocked, true, 'was unlocked')
+ assert.equal(resultingState.metamask.isInitialized, true, 'was initialized')
+ });
+});
diff --git a/test/unit/actions/set_selected_account_test.js b/test/unit/actions/set_selected_account_test.js
new file mode 100644
index 000000000..0487bc5f0
--- /dev/null
+++ b/test/unit/actions/set_selected_account_test.js
@@ -0,0 +1,28 @@
+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('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)
+
+ const action = {
+ type: actions.SET_SELECTED_ACCOUNT,
+ value: 'bar',
+ }
+ freeze(action)
+
+ var resultingState = reducers(initialState, action)
+ assert.equal(resultingState.appState.activeAddress, action.value)
+ });
+});
diff --git a/test/unit/actions/tx_test.js b/test/unit/actions/tx_test.js
new file mode 100644
index 000000000..b15bee393
--- /dev/null
+++ b/test/unit/actions/tx_test.js
@@ -0,0 +1,168 @@
+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('tx confirmation screen', function() {
+ var initialState, result
+
+ describe('when there is only one tx', function() {
+ var firstTxId = 1457634084250832
+
+ beforeEach(function() {
+
+ initialState = {
+ appState: {
+ currentView: {
+ name: 'confTx',
+ },
+ },
+ metamask: {
+ unconfTxs: {
+ '1457634084250832': {
+ id: 1457634084250832,
+ status: "unconfirmed",
+ time: 1457634084250,
+ }
+ },
+ }
+ }
+ freeze(initialState)
+ })
+
+ describe('cancelTx', function() {
+
+ before(function(done) {
+ actions._setAccountManager({
+ approveTransaction(txId, cb) { cb('An error!') },
+ cancelTransaction(txId) { /* noop */ },
+ clearSeedWordCache(cb) { cb() },
+ })
+
+ actions.cancelTx({id: firstTxId})(function(action) {
+ result = reducers(initialState, action)
+ done()
+ })
+ })
+
+ it('should transition to the accounts list', function() {
+ assert.equal(result.appState.currentView.name, 'accounts')
+ })
+
+ 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._setAccountManager({
+ approveTransaction(txId, cb) { cb('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() {
+ before(function(done) {
+ actions._setAccountManager({
+ approveTransaction(txId, cb) { cb() },
+ })
+
+ actions.sendTx({id: firstTxId})(function(action) {
+ result = reducers(initialState, action)
+ done()
+ })
+ })
+
+ it('should navigate away from the tx page', function() {
+ assert.equal(result.appState.currentView.name, 'accounts')
+ })
+
+ it('should clear the tx from the unconfirmed transactions', function() {
+ assert(!(firstTxId in result.metamask.unconfTxs), 'tx is cleared')
+ })
+ })
+ })
+
+ describe('when there are two pending txs', function() {
+ var firstTxId = 1457634084250832
+ var result, initialState
+ before(function(done) {
+ initialState = {
+ appState: {
+ currentView: {
+ name: 'confTx',
+ },
+ },
+ metamask: {
+ unconfTxs: {
+ '1457634084250832': {
+ id: 1457634084250832,
+ status: "unconfirmed",
+ time: 1457634084250,
+ },
+ '1457634084250833': {
+ id: 1457634084250833,
+ status: "unconfirmed",
+ time: 1457634084255,
+ },
+ },
+ }
+ }
+ freeze(initialState)
+
+
+ actions._setAccountManager({
+ approveTransaction(txId, cb) { cb() },
+ })
+
+ 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)
+ })
+
+ it('should only have one unconfirmed tx remaining', function() {
+ var count = getUnconfirmedTxCount(result)
+ assert.equal(count, 1)
+ })
+ })
+ })
+});
+
+function getUnconfirmedTxCount(state) {
+ var txs = state.metamask.unconfTxs
+ 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
new file mode 100644
index 000000000..0558c6e42
--- /dev/null
+++ b/test/unit/actions/view_info_test.js
@@ -0,0 +1,23 @@
+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('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
new file mode 100644
index 000000000..37be9ee85
--- /dev/null
+++ b/test/unit/actions/warning_test.js
@@ -0,0 +1,24 @@
+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('action DISPLAY_WARNING', function() {
+
+ it('sets appState.warning to provided value', function() {
+ var initialState = {
+ appState: {},
+ }
+ freeze(initialState)
+
+ const warningText = 'This is a sample warning message'
+
+ const action = actions.displayWarning(warningText)
+ const resultingState = reducers(initialState, action)
+
+ assert.equal(resultingState.appState.warning, warningText, 'warning text set')
+ });
+});
diff --git a/test/unit/migrations-test.js b/test/unit/migrations-test.js
index 3429ffb1d..092c0eccd 100644
--- a/test/unit/migrations-test.js
+++ b/test/unit/migrations-test.js
@@ -1,5 +1,4 @@
var assert = require('assert')
-var test = require('tape')
var path = require('path')
var wallet1 = require(path.join('..', 'lib', 'migrations', '001.json'))
diff --git a/test/unit/util_test.js b/test/unit/util_test.js
new file mode 100644
index 000000000..7f8103d3b
--- /dev/null
+++ b/test/unit/util_test.js
@@ -0,0 +1,114 @@
+var assert = require('assert')
+var sinon = require('sinon')
+const ethUtil = require('ethereumjs-util')
+
+var path = require('path')
+var util = require(path.join(__dirname, '..', '..', 'ui', 'app', 'util.js'))
+
+describe('util', function() {
+ var ethInWei = '1'
+ for (var i = 0; i < 18; i++ ) { ethInWei += '0' }
+
+ beforeEach(function() {
+ this.sinon = sinon.sandbox.create()
+ })
+
+ afterEach(function() {
+ this.sinon.restore()
+ })
+
+ 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() {
+ var result = util.numericBalance('0x012')
+ assert.equal(result.toString(10), '18')
+ })
+
+ it('should work with no hex prefix', function() {
+ var result = util.numericBalance('012')
+ assert.equal(result.toString(10), '18')
+ })
+
+ })
+
+ describe('#ethToWei', function() {
+
+ it('should take an eth BN, returns wei BN', function() {
+ var input = new ethUtil.BN(1, 10)
+ var result = util.ethToWei(input)
+ assert.equal(result, ethInWei, '18 zeroes')
+ })
+
+ })
+
+ describe('#weiToEth', function() {
+
+ it('should take a wei BN and return an eth BN', function() {
+ var result = util.weiToEth(new ethUtil.BN(ethInWei))
+ assert.equal(result, '1', 'equals 1 eth')
+ })
+
+ })
+
+ 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() {
+ var input = new ethUtil.BN(ethInWei, 10).toJSON()
+ var result = util.formatBalance(input)
+ assert.equal(result, '1.0000 ETH')
+ })
+
+ 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)
+ assert.equal(result, '0.5000 ETH')
+ })
+
+ it('should display four decimal points', function() {
+ var input = "0x128dfa6a90b28000"
+ var result = util.formatBalance(input)
+ assert.equal(result, '1.3370 ETH')
+ })
+
+ })
+
+ describe('#normalizeToWei', function() {
+ it('should convert an eth to the appropriate equivalent values', function() {
+ var valueTable = {
+ wei: '1000000000000000000',
+ kwei: '1000000000000000',
+ mwei: '1000000000000',
+ gwei: '1000000000',
+ szabo: '1000000',
+ finney:'1000',
+ ether: '1',
+ kether:'0.001',
+ mether:'0.000001',
+ // AUDIT: We're getting BN numbers on these ones.
+ // I think they're big enough to ignore for now.
+ // gether:'0.000000001',
+ // tether:'0.000000000001',
+ }
+ var oneEthBn = new ethUtil.BN(ethInWei, 10)
+
+ 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}`)
+
+ }
+ })
+ })
+
+})