mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Unit tests for containers, utils and selectors in send_/
This commit is contained in:
parent
e869d09c79
commit
7c49009854
@ -9,7 +9,7 @@
|
|||||||
"dist": "gulp dist",
|
"dist": "gulp dist",
|
||||||
"doc": "jsdoc -c development/tools/.jsdoc.json",
|
"doc": "jsdoc -c development/tools/.jsdoc.json",
|
||||||
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
||||||
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\"",
|
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.js\"",
|
||||||
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
|
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
|
||||||
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
|
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
|
||||||
"test:integration:build": "gulp build:scss",
|
"test:integration:build": "gulp build:scss",
|
||||||
@ -258,6 +258,7 @@
|
|||||||
"open": "0.0.5",
|
"open": "0.0.5",
|
||||||
"png-file-stream": "^1.0.0",
|
"png-file-stream": "^1.0.0",
|
||||||
"prompt": "^1.0.0",
|
"prompt": "^1.0.0",
|
||||||
|
"proxyquire": "2.0.1",
|
||||||
"qs": "^6.2.0",
|
"qs": "^6.2.0",
|
||||||
"qunitjs": "^2.4.1",
|
"qunitjs": "^2.4.1",
|
||||||
"radgrad-jsdoc-template": "^1.1.3",
|
"radgrad-jsdoc-template": "^1.1.3",
|
||||||
|
@ -16,11 +16,11 @@ const {
|
|||||||
MIN_GAS_PRICE_DEC,
|
MIN_GAS_PRICE_DEC,
|
||||||
MIN_GAS_LIMIT_DEC,
|
MIN_GAS_LIMIT_DEC,
|
||||||
MIN_GAS_PRICE_GWEI,
|
MIN_GAS_PRICE_GWEI,
|
||||||
} = require('../send/send-constants')
|
} = require('../send_/send.constants')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
} = require('../send/send-utils')
|
} = require('../send_/send.utils')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
conversionUtil,
|
conversionUtil,
|
||||||
|
@ -149,10 +149,10 @@ AddTokenScreen.prototype.validate = function () {
|
|||||||
errors.customAddress = this.context.t('invalidAddress')
|
errors.customAddress = this.context.t('invalidAddress')
|
||||||
}
|
}
|
||||||
|
|
||||||
const validDecimals = customDecimals !== null
|
const validDecimals = customDecimals !== null &&
|
||||||
&& customDecimals !== ''
|
customDecimals !== '' &&
|
||||||
&& customDecimals >= 0
|
customDecimals >= 0 &&
|
||||||
&& customDecimals < 36
|
customDecimals < 36
|
||||||
if (!validDecimals) {
|
if (!validDecimals) {
|
||||||
errors.customDecimals = this.context.t('decimalsMustZerotoTen')
|
errors.customDecimals = this.context.t('decimalsMustZerotoTen')
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ const { conversionUtil } = require('../../conversion-util')
|
|||||||
const SenderToRecipient = require('../sender-to-recipient')
|
const SenderToRecipient = require('../sender-to-recipient')
|
||||||
const NetworkDisplay = require('../network-display')
|
const NetworkDisplay = require('../network-display')
|
||||||
|
|
||||||
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
|
const { MIN_GAS_PRICE_HEX } = require('../send_/send.constants')
|
||||||
|
|
||||||
class ConfirmDeployContract extends Component {
|
class ConfirmDeployContract extends Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
|
@ -17,16 +17,16 @@ const {
|
|||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
} = require('../../conversion-util')
|
} = require('../../conversion-util')
|
||||||
const {
|
const {
|
||||||
getGasTotal,
|
calcGasTotal,
|
||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
} = require('../send/send-utils')
|
} = require('../send_/send.utils')
|
||||||
const GasFeeDisplay = require('../send/gas-fee-display-v2')
|
const GasFeeDisplay = require('../send/gas-fee-display-v2')
|
||||||
const SenderToRecipient = require('../sender-to-recipient')
|
const SenderToRecipient = require('../sender-to-recipient')
|
||||||
const NetworkDisplay = require('../network-display')
|
const NetworkDisplay = require('../network-display')
|
||||||
const currencyFormatter = require('currency-formatter')
|
const currencyFormatter = require('currency-formatter')
|
||||||
const currencies = require('currency-formatter/currencies')
|
const currencies = require('currency-formatter/currencies')
|
||||||
|
|
||||||
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
|
const { MIN_GAS_PRICE_HEX } = require('../send_/send.constants')
|
||||||
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
|
const { SEND_ROUTE, DEFAULT_ROUTE } = require('../../routes')
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -590,7 +590,7 @@ ConfirmSendEther.prototype.isBalanceSufficient = function (txMeta) {
|
|||||||
value: amount,
|
value: amount,
|
||||||
},
|
},
|
||||||
} = txMeta
|
} = txMeta
|
||||||
const gasTotal = getGasTotal(gas, gasPrice)
|
const gasTotal = calcGasTotal(gas, gasPrice)
|
||||||
|
|
||||||
return isBalanceSufficient({
|
return isBalanceSufficient({
|
||||||
amount,
|
amount,
|
||||||
|
@ -20,9 +20,9 @@ const {
|
|||||||
addCurrencies,
|
addCurrencies,
|
||||||
} = require('../../conversion-util')
|
} = require('../../conversion-util')
|
||||||
const {
|
const {
|
||||||
getGasTotal,
|
calcGasTotal,
|
||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
} = require('../send/send-utils')
|
} = require('../send_/send.utils')
|
||||||
const {
|
const {
|
||||||
calcTokenAmount,
|
calcTokenAmount,
|
||||||
} = require('../../token-util')
|
} = require('../../token-util')
|
||||||
@ -30,7 +30,7 @@ const classnames = require('classnames')
|
|||||||
const currencyFormatter = require('currency-formatter')
|
const currencyFormatter = require('currency-formatter')
|
||||||
const currencies = require('currency-formatter/currencies')
|
const currencies = require('currency-formatter/currencies')
|
||||||
|
|
||||||
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
|
const { MIN_GAS_PRICE_HEX } = require('../send_/send.constants')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getTokenExchangeRate,
|
getTokenExchangeRate,
|
||||||
@ -580,7 +580,7 @@ ConfirmSendToken.prototype.isBalanceSufficient = function (txMeta) {
|
|||||||
gasPrice,
|
gasPrice,
|
||||||
},
|
},
|
||||||
} = txMeta
|
} = txMeta
|
||||||
const gasTotal = getGasTotal(gas, gasPrice)
|
const gasTotal = calcGasTotal(gas, gasPrice)
|
||||||
|
|
||||||
return isBalanceSufficient({
|
return isBalanceSufficient({
|
||||||
amount: '0',
|
amount: '0',
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
|
||||||
|
proxyquire('../account-list-item.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../send.selectors.js': {
|
||||||
|
getConversionRate: (s) => `mockConversionRate:${s}`,
|
||||||
|
getConvertedCurrency: (s) => `mockCurrentCurrency:${s}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('account-list-item container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
conversionRate: 'mockConversionRate:mockState',
|
||||||
|
currentCurrency: 'mockCurrentCurrency:mockState',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,91 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
setMaxModeTo: sinon.spy(),
|
||||||
|
updateSendAmount: sinon.spy(),
|
||||||
|
}
|
||||||
|
const duckActionSpies = {
|
||||||
|
updateSendErrors: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../amount-max-button.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../../send.selectors.js': {
|
||||||
|
getGasTotal: (s) => `mockGasTotal:${s}`,
|
||||||
|
getSelectedToken: (s) => `mockSelectedToken:${s}`,
|
||||||
|
getSendFromBalance: (s) => `mockBalance:${s}`,
|
||||||
|
getTokenBalance: (s) => `mockTokenBalance:${s}`,
|
||||||
|
},
|
||||||
|
'./amount-max-button.selectors.js': { getMaxModeOn: (s) => `mockMaxModeOn:${s}` },
|
||||||
|
'./amount-max-button.utils.js': { calcMaxAmount: (mockObj) => mockObj.val + 1 },
|
||||||
|
'../../../../../actions': actionSpies,
|
||||||
|
'../../../../../ducks/send': duckActionSpies,
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('amount-max-button container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
balance: 'mockBalance:mockState',
|
||||||
|
gasTotal: 'mockGasTotal:mockState',
|
||||||
|
maxModeOn: 'mockMaxModeOn:mockState',
|
||||||
|
selectedToken: 'mockSelectedToken:mockState',
|
||||||
|
tokenBalance: 'mockTokenBalance:mockState',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('setAmountToMax()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.setAmountToMax({ val: 11, foo: 'bar' })
|
||||||
|
assert(dispatchSpy.calledTwice)
|
||||||
|
assert(duckActionSpies.updateSendErrors.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
duckActionSpies.updateSendErrors.getCall(0).args[0],
|
||||||
|
{ amount: null }
|
||||||
|
)
|
||||||
|
assert(actionSpies.updateSendAmount.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
actionSpies.updateSendAmount.getCall(0).args[0],
|
||||||
|
12
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('setMaxModeTo()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.setMaxModeTo('mockVal')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
actionSpies.setMaxModeTo.getCall(0).args[0],
|
||||||
|
'mockVal'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,22 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import {
|
||||||
|
getMaxModeOn,
|
||||||
|
} from '../amount-max-button.selectors.js'
|
||||||
|
|
||||||
|
describe('amount-max-button selectors', () => {
|
||||||
|
|
||||||
|
describe('getMaxModeOn()', () => {
|
||||||
|
it('should', () => {
|
||||||
|
const state = {
|
||||||
|
metamask: {
|
||||||
|
send: {
|
||||||
|
maxModeOn: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(getMaxModeOn(state), null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,27 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import {
|
||||||
|
calcMaxAmount,
|
||||||
|
} from '../amount-max-button.utils.js'
|
||||||
|
|
||||||
|
describe('amount-max-button utils', () => {
|
||||||
|
|
||||||
|
describe('calcMaxAmount()', () => {
|
||||||
|
it('should calculate the correct amount when no selectedToken defined', () => {
|
||||||
|
assert.deepEqual(calcMaxAmount({
|
||||||
|
balance: 'ffffff',
|
||||||
|
gasTotal: 'ff',
|
||||||
|
selectedToken: false,
|
||||||
|
}), 'ffff00')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should calculate the correct amount when a selectedToken is defined', () => {
|
||||||
|
assert.deepEqual(calcMaxAmount({
|
||||||
|
selectedToken: {
|
||||||
|
decimals: 10,
|
||||||
|
},
|
||||||
|
tokenBalance: 100,
|
||||||
|
}), 'e8d4a51000')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,109 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
setMaxModeTo: sinon.spy(),
|
||||||
|
updateSendAmount: sinon.spy(),
|
||||||
|
}
|
||||||
|
const duckActionSpies = {
|
||||||
|
updateSendErrors: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../send-amount-row.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../send.selectors': {
|
||||||
|
getAmountConversionRate: (s) => `mockAmountConversionRate:${s}`,
|
||||||
|
getConversionRate: (s) => `mockConversionRate:${s}`,
|
||||||
|
getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
|
||||||
|
getGasTotal: (s) => `mockGasTotal:${s}`,
|
||||||
|
getPrimaryCurrency: (s) => `mockPrimaryCurrency:${s}`,
|
||||||
|
getSelectedToken: (s) => `mockSelectedToken:${s}`,
|
||||||
|
getSendAmount: (s) => `mockAmount:${s}`,
|
||||||
|
getSendFromBalance: (s) => `mockBalance:${s}`,
|
||||||
|
getTokenBalance: (s) => `mockTokenBalance:${s}`,
|
||||||
|
},
|
||||||
|
'./send-amount-row.selectors': { sendAmountIsInError: (s) => `mockInError:${s}` },
|
||||||
|
'../../send.utils': { getAmountErrorObject: (mockDataObject) => ({ ...mockDataObject, mockChange: true }) },
|
||||||
|
'../../../../actions': actionSpies,
|
||||||
|
'../../../../ducks/send': duckActionSpies,
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send-amount-row container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
amount: 'mockAmount:mockState',
|
||||||
|
amountConversionRate: 'mockAmountConversionRate:mockState',
|
||||||
|
balance: 'mockBalance:mockState',
|
||||||
|
conversionRate: 'mockConversionRate:mockState',
|
||||||
|
convertedCurrency: 'mockConvertedCurrency:mockState',
|
||||||
|
gasTotal: 'mockGasTotal:mockState',
|
||||||
|
inError: 'mockInError:mockState',
|
||||||
|
primaryCurrency: 'mockPrimaryCurrency:mockState',
|
||||||
|
selectedToken: 'mockSelectedToken:mockState',
|
||||||
|
tokenBalance: 'mockTokenBalance:mockState',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('setMaxModeTo()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.setMaxModeTo('mockBool')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(actionSpies.setMaxModeTo.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
actionSpies.setMaxModeTo.getCall(0).args[0],
|
||||||
|
'mockBool'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateSendAmount()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.updateSendAmount('mockAmount')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(actionSpies.updateSendAmount.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
actionSpies.updateSendAmount.getCall(0).args[0],
|
||||||
|
'mockAmount'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateSendAmountError()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.updateSendAmountError({ some: 'data' })
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(duckActionSpies.updateSendErrors.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
duckActionSpies.updateSendErrors.getCall(0).args[0],
|
||||||
|
{ some: 'data', mockChange: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,34 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import {
|
||||||
|
sendAmountIsInError,
|
||||||
|
} from '../send-amount-row.selectors.js'
|
||||||
|
|
||||||
|
describe('send-amount-row selectors', () => {
|
||||||
|
|
||||||
|
describe('sendAmountIsInError()', () => {
|
||||||
|
it('should return true if send.errors.amount is truthy', () => {
|
||||||
|
const state = {
|
||||||
|
send: {
|
||||||
|
errors: {
|
||||||
|
amount: 'abc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(sendAmountIsInError(state), true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false if send.errors.amount is falsy', () => {
|
||||||
|
const state = {
|
||||||
|
send: {
|
||||||
|
errors: {
|
||||||
|
amount: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(sendAmountIsInError(state), false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -8,7 +8,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
getFromDropdownOpen,
|
getFromDropdownOpen,
|
||||||
} from './send-from-row.selectors.js'
|
} from './send-from-row.selectors.js'
|
||||||
import { calcTokenUpdateAmount } from './send-from-row.utils.js'
|
import { calcTokenBalance } from '../../send.utils.js'
|
||||||
import {
|
import {
|
||||||
updateSendFrom,
|
updateSendFrom,
|
||||||
setSendTokenBalance,
|
setSendTokenBalance,
|
||||||
@ -39,7 +39,7 @@ function mapDispatchToProps (dispatch) {
|
|||||||
setSendTokenBalance: (usersToken, selectedToken) => {
|
setSendTokenBalance: (usersToken, selectedToken) => {
|
||||||
if (!usersToken) return
|
if (!usersToken) return
|
||||||
|
|
||||||
const tokenBalance = calcTokenUpdateAmount(selectedToken, selectedToken)
|
const tokenBalance = calcTokenBalance(usersToken, selectedToken)
|
||||||
dispatch(setSendTokenBalance(tokenBalance))
|
dispatch(setSendTokenBalance(tokenBalance))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
const {
|
|
||||||
calcTokenAmount,
|
|
||||||
} = require('../../../../token-util')
|
|
||||||
|
|
||||||
function calcTokenUpdateAmount (usersToken, selectedToken) {
|
|
||||||
const { decimals } = selectedToken || {}
|
|
||||||
return calcTokenAmount(usersToken.balance.toString(), decimals)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
calcTokenUpdateAmount,
|
|
||||||
}
|
|
@ -0,0 +1,110 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
updateSendFrom: sinon.spy(),
|
||||||
|
setSendTokenBalance: sinon.spy(),
|
||||||
|
}
|
||||||
|
const duckActionSpies = {
|
||||||
|
closeFromDropdown: sinon.spy(),
|
||||||
|
openFromDropdown: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../send-from-row.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../send.selectors.js': {
|
||||||
|
accountsWithSendEtherInfoSelector: (s) => `mockFromAccounts:${s}`,
|
||||||
|
getConversionRate: (s) => `mockConversionRate:${s}`,
|
||||||
|
getSelectedTokenContract: (s) => `mockTokenContract:${s}`,
|
||||||
|
getSendFromObject: (s) => `mockFrom:${s}`,
|
||||||
|
},
|
||||||
|
'./send-from-row.selectors.js': { getFromDropdownOpen: (s) => `mockFromDropdownOpen:${s}` },
|
||||||
|
'../../send.utils.js': { calcTokenBalance: (a, b) => a + b },
|
||||||
|
'../../../../actions': actionSpies,
|
||||||
|
'../../../../ducks/send': duckActionSpies,
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send-from-row container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
conversionRate: 'mockConversionRate:mockState',
|
||||||
|
from: 'mockFrom:mockState',
|
||||||
|
fromAccounts: 'mockFromAccounts:mockState',
|
||||||
|
fromDropdownOpen: 'mockFromDropdownOpen:mockState',
|
||||||
|
tokenContract: 'mockTokenContract:mockState',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('closeFromDropdown()', () => {
|
||||||
|
it('should dispatch a closeFromDropdown action', () => {
|
||||||
|
mapDispatchToPropsObject.closeFromDropdown()
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(duckActionSpies.closeFromDropdown.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
duckActionSpies.closeFromDropdown.getCall(0).args[0],
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('openFromDropdown()', () => {
|
||||||
|
it('should dispatch a openFromDropdown action', () => {
|
||||||
|
mapDispatchToPropsObject.openFromDropdown()
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(duckActionSpies.openFromDropdown.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
duckActionSpies.openFromDropdown.getCall(0).args[0],
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateSendFrom()', () => {
|
||||||
|
it('should dispatch an updateSendFrom action', () => {
|
||||||
|
mapDispatchToPropsObject.updateSendFrom('mockFrom')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
actionSpies.updateSendFrom.getCall(0).args[0],
|
||||||
|
'mockFrom'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('setSendTokenBalance()', () => {
|
||||||
|
it('should dispatch an setSendTokenBalance action', () => {
|
||||||
|
mapDispatchToPropsObject.setSendTokenBalance('mockUsersToken', 'mockSelectedToken')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
actionSpies.setSendTokenBalance.getCall(0).args[0],
|
||||||
|
'mockUsersTokenmockSelectedToken'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,20 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import {
|
||||||
|
getFromDropdownOpen,
|
||||||
|
} from '../send-from-row.selectors.js'
|
||||||
|
|
||||||
|
describe('send-from-row selectors', () => {
|
||||||
|
|
||||||
|
describe('getFromDropdownOpen()', () => {
|
||||||
|
it('should get send.fromDropdownOpen', () => {
|
||||||
|
const state = {
|
||||||
|
send: {
|
||||||
|
fromDropdownOpen: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(getFromDropdownOpen(state), null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -5,5 +5,5 @@ const selectors = {
|
|||||||
module.exports = selectors
|
module.exports = selectors
|
||||||
|
|
||||||
function sendGasIsInError (state) {
|
function sendGasIsInError (state) {
|
||||||
return state.metamask.send.errors.gasLoading
|
return state.send.errors.gasLoading
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
showModal: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../send-gas-row.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../send.selectors.js': {
|
||||||
|
getConversionRate: (s) => `mockConversionRate:${s}`,
|
||||||
|
getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
|
||||||
|
getGasTotal: (s) => `mockGasTotal:${s}`,
|
||||||
|
},
|
||||||
|
'./send-gas-row.selectors.js': { sendGasIsInError: (s) => `mockGasLoadingError:${s}` },
|
||||||
|
'../../../../actions': actionSpies,
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send-gas-row container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
conversionRate: 'mockConversionRate:mockState',
|
||||||
|
convertedCurrency: 'mockConvertedCurrency:mockState',
|
||||||
|
gasTotal: 'mockGasTotal:mockState',
|
||||||
|
gasLoadingError: 'mockGasLoadingError:mockState',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('showCustomizeGasModal()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.showCustomizeGasModal()
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
actionSpies.showModal.getCall(0).args[0],
|
||||||
|
{ name: 'CUSTOMIZE_GAS' }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,22 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import {
|
||||||
|
sendGasIsInError,
|
||||||
|
} from '../send-gas-row.selectors.js'
|
||||||
|
|
||||||
|
describe('send-gas-row selectors', () => {
|
||||||
|
|
||||||
|
describe('sendGasIsInError()', () => {
|
||||||
|
it('should return send.errors.gasLoading', () => {
|
||||||
|
const state = {
|
||||||
|
send: {
|
||||||
|
errors: {
|
||||||
|
gasLoading: 'abc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(sendGasIsInError(state), 'abc')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,28 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
|
||||||
|
proxyquire('../send-row-error-message.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../../send.selectors': { getSendErrors: (s) => `mockErrors:${s}` },
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send-row-error-message container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState', { errorType: 'someType' }), {
|
||||||
|
errors: 'mockErrors:mockState',
|
||||||
|
errorType: 'someType' })
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,114 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
updateSendTo: sinon.spy(),
|
||||||
|
}
|
||||||
|
const duckActionSpies = {
|
||||||
|
closeToDropdown: sinon.spy(),
|
||||||
|
openToDropdown: sinon.spy(),
|
||||||
|
updateSendErrors: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../send-to-row.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../send.selectors.js': {
|
||||||
|
getCurrentNetwork: (s) => `mockNetwork:${s}`,
|
||||||
|
getSendTo: (s) => `mockTo:${s}`,
|
||||||
|
getSendToAccounts: (s) => `mockToAccounts:${s}`,
|
||||||
|
},
|
||||||
|
'./send-to-row.selectors.js': {
|
||||||
|
getToDropdownOpen: (s) => `mockToDropdownOpen:${s}`,
|
||||||
|
sendToIsInError: (s) => `mockInError:${s}`,
|
||||||
|
},
|
||||||
|
'./send-to-row.utils.js': { getToErrorObject: (t) => `mockError:${t}` },
|
||||||
|
'../../../../actions': actionSpies,
|
||||||
|
'../../../../ducks/send': duckActionSpies,
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send-to-row container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
inError: 'mockInError:mockState',
|
||||||
|
network: 'mockNetwork:mockState',
|
||||||
|
to: 'mockTo:mockState',
|
||||||
|
toAccounts: 'mockToAccounts:mockState',
|
||||||
|
toDropdownOpen: 'mockToDropdownOpen:mockState',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('closeToDropdown()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.closeToDropdown()
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(duckActionSpies.closeToDropdown.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
duckActionSpies.closeToDropdown.getCall(0).args[0],
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('openToDropdown()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.openToDropdown()
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(duckActionSpies.openToDropdown.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
duckActionSpies.openToDropdown.getCall(0).args[0],
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateSendTo()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.updateSendTo('mockTo', 'mockNickname')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(actionSpies.updateSendTo.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
actionSpies.updateSendTo.getCall(0).args,
|
||||||
|
['mockTo', 'mockNickname']
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateSendToError()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.updateSendToError('mockTo')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(duckActionSpies.updateSendErrors.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
duckActionSpies.updateSendErrors.getCall(0).args[0],
|
||||||
|
'mockError:mockTo'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,47 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import {
|
||||||
|
getToDropdownOpen,
|
||||||
|
sendToIsInError,
|
||||||
|
} from '../send-to-row.selectors.js'
|
||||||
|
|
||||||
|
describe('send-to-row selectors', () => {
|
||||||
|
|
||||||
|
describe('getToDropdownOpen()', () => {
|
||||||
|
it('should return send.getToDropdownOpen', () => {
|
||||||
|
const state = {
|
||||||
|
send: {
|
||||||
|
toDropdownOpen: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(getToDropdownOpen(state), false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sendToIsInError()', () => {
|
||||||
|
it('should return true if send.errors.to is truthy', () => {
|
||||||
|
const state = {
|
||||||
|
send: {
|
||||||
|
errors: {
|
||||||
|
to: 'abc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(sendToIsInError(state), true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false if send.errors.to is falsy', () => {
|
||||||
|
const state = {
|
||||||
|
send: {
|
||||||
|
errors: {
|
||||||
|
to: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(sendToIsInError(state), false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,45 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
import {
|
||||||
|
REQUIRED_ERROR,
|
||||||
|
INVALID_RECIPIENT_ADDRESS_ERROR,
|
||||||
|
} from '../../../send.constants'
|
||||||
|
|
||||||
|
const stubs = {
|
||||||
|
isValidAddress: sinon.stub().callsFake(to => Boolean(to.match(/^[0xabcdef123456798]+$/))),
|
||||||
|
}
|
||||||
|
|
||||||
|
const toRowUtils = proxyquire('../send-to-row.utils.js', {
|
||||||
|
'../../../../util': {
|
||||||
|
isValidAddress: stubs.isValidAddress,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
getToErrorObject,
|
||||||
|
} = toRowUtils
|
||||||
|
|
||||||
|
describe('send-to-row utils', () => {
|
||||||
|
|
||||||
|
describe('getToErrorObject()', () => {
|
||||||
|
it('should return a required error if to is falsy', () => {
|
||||||
|
assert.deepEqual(getToErrorObject(null), {
|
||||||
|
to: REQUIRED_ERROR,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return an invalid recipient error if to is truthy but invalid', () => {
|
||||||
|
assert.deepEqual(getToErrorObject('mockInvalidTo'), {
|
||||||
|
to: INVALID_RECIPIENT_ADDRESS_ERROR,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return null if to is truthy and valid', () => {
|
||||||
|
assert.deepEqual(getToErrorObject('0xabc123'), {
|
||||||
|
to: null,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -20,10 +20,8 @@ import {
|
|||||||
getSendToAccounts,
|
getSendToAccounts,
|
||||||
getTokenBalance,
|
getTokenBalance,
|
||||||
getUnapprovedTxs,
|
getUnapprovedTxs,
|
||||||
} from '../send.selectors'
|
|
||||||
import {
|
|
||||||
isSendFormInError,
|
isSendFormInError,
|
||||||
} from './send-footer.selectors'
|
} from '../send.selectors'
|
||||||
import {
|
import {
|
||||||
addressIsNew,
|
addressIsNew,
|
||||||
constructTxParams,
|
constructTxParams,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getSendErrors } from '../send.selectors'
|
const { getSendErrors } = require('../send.selectors')
|
||||||
|
|
||||||
const selectors = {
|
const selectors = {
|
||||||
isSendFormInError,
|
isSendFormInError,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import ethAbi from 'ethereumjs-abi'
|
const ethAbi = require('ethereumjs-abi')
|
||||||
import ethUtil from 'ethereumjs-util'
|
const ethUtil = require('ethereumjs-util')
|
||||||
import { TOKEN_TRANSFER_FUNCTION_SIGNATURE } from '../send.constants'
|
const { TOKEN_TRANSFER_FUNCTION_SIGNATURE } = require('../send.constants')
|
||||||
|
|
||||||
function formShouldBeDisabled ({ inError, selectedToken, tokenBalance, gasTotal }) {
|
function formShouldBeDisabled ({ inError, selectedToken, tokenBalance, gasTotal }) {
|
||||||
const missingTokenBalance = selectedToken && !tokenBalance
|
const missingTokenBalance = selectedToken && !tokenBalance
|
||||||
@ -47,6 +47,7 @@ function constructUpdatedTx ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedToken) {
|
if (selectedToken) {
|
||||||
|
console.log(`ethAbi.rawEncode`, ethAbi.rawEncode)
|
||||||
const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
|
const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
|
||||||
ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
|
ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]),
|
||||||
x => ('00' + x.toString(16)).slice(-2)
|
x => ('00' + x.toString(16)).slice(-2)
|
||||||
@ -70,6 +71,8 @@ function constructUpdatedTx ({
|
|||||||
delete editingTx.txParams.data
|
delete editingTx.txParams.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return editingTx
|
||||||
}
|
}
|
||||||
|
|
||||||
function addressIsNew (toAccounts, newAddress) {
|
function addressIsNew (toAccounts, newAddress) {
|
||||||
@ -81,4 +84,5 @@ module.exports = {
|
|||||||
formShouldBeDisabled,
|
formShouldBeDisabled,
|
||||||
constructTxParams,
|
constructTxParams,
|
||||||
constructUpdatedTx,
|
constructUpdatedTx,
|
||||||
|
addHexPrefixToObjectValues,
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,202 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
addToAddressBook: sinon.spy(),
|
||||||
|
clearSend: sinon.spy(),
|
||||||
|
signTokenTx: sinon.spy(),
|
||||||
|
signTx: sinon.spy(),
|
||||||
|
updateTransaction: sinon.spy(),
|
||||||
|
}
|
||||||
|
const utilsStubs = {
|
||||||
|
addressIsNew: sinon.stub().returns(true),
|
||||||
|
constructTxParams: sinon.stub().returns('mockConstructedTxParams'),
|
||||||
|
constructUpdatedTx: sinon.stub().returns('mockConstructedUpdatedTxParams'),
|
||||||
|
formShouldBeDisabled: sinon.stub().returns('mockFormShouldBeDisabled'),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../send-footer.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../../actions': actionSpies,
|
||||||
|
'../send.selectors': {
|
||||||
|
getGasLimit: (s) => `mockGasLimit:${s}`,
|
||||||
|
getGasPrice: (s) => `mockGasPrice:${s}`,
|
||||||
|
getGasTotal: (s) => `mockGasTotal:${s}`,
|
||||||
|
getSelectedToken: (s) => `mockSelectedToken:${s}`,
|
||||||
|
getSendAmount: (s) => `mockAmount:${s}`,
|
||||||
|
getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`,
|
||||||
|
getSendFromObject: (s) => `mockFromObject:${s}`,
|
||||||
|
getSendTo: (s) => `mockTo:${s}`,
|
||||||
|
getSendToAccounts: (s) => `mockToAccounts:${s}`,
|
||||||
|
getTokenBalance: (s) => `mockTokenBalance:${s}`,
|
||||||
|
getUnapprovedTxs: (s) => `mockUnapprovedTxs:${s}`,
|
||||||
|
isSendFormInError: (s) => `mockInError:${s}`,
|
||||||
|
},
|
||||||
|
'./send-footer.selectors': { isSendFormInError: () => {} },
|
||||||
|
'./send-footer.utils': utilsStubs,
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send-footer container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
amount: 'mockAmount:mockState',
|
||||||
|
disabled: 'mockFormShouldBeDisabled',
|
||||||
|
selectedToken: 'mockSelectedToken:mockState',
|
||||||
|
editingTransactionId: 'mockEditingTransactionId:mockState',
|
||||||
|
from: 'mockFromObject:mockState',
|
||||||
|
gasLimit: 'mockGasLimit:mockState',
|
||||||
|
gasPrice: 'mockGasPrice:mockState',
|
||||||
|
inError: 'mockInError:mockState',
|
||||||
|
isToken: true,
|
||||||
|
to: 'mockTo:mockState',
|
||||||
|
toAccounts: 'mockToAccounts:mockState',
|
||||||
|
unapprovedTxs: 'mockUnapprovedTxs:mockState',
|
||||||
|
})
|
||||||
|
assert.deepEqual(
|
||||||
|
utilsStubs.formShouldBeDisabled.getCall(0).args[0],
|
||||||
|
{
|
||||||
|
inError: 'mockInError:mockState',
|
||||||
|
selectedToken: 'mockSelectedToken:mockState',
|
||||||
|
tokenBalance: 'mockTokenBalance:mockState',
|
||||||
|
gasTotal: 'mockGasTotal:mockState',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('clearSend()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.clearSend()
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(actionSpies.clearSend.calledOnce)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sign()', () => {
|
||||||
|
it('should dispatch a signTokenTx action if selectedToken is defined', () => {
|
||||||
|
mapDispatchToPropsObject.sign({
|
||||||
|
selectedToken: {
|
||||||
|
address: '0xabc',
|
||||||
|
},
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
})
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
utilsStubs.constructTxParams.getCall(0).args[0],
|
||||||
|
{
|
||||||
|
selectedToken: {
|
||||||
|
address: '0xabc',
|
||||||
|
},
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert.deepEqual(
|
||||||
|
actionSpies.signTokenTx.getCall(0).args,
|
||||||
|
[ '0xabc', 'mockTo', 'mockAmount', 'mockConstructedTxParams' ]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should dispatch a sign action if selectedToken is not defined', () => {
|
||||||
|
utilsStubs.constructTxParams.resetHistory()
|
||||||
|
mapDispatchToPropsObject.sign({
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
})
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
utilsStubs.constructTxParams.getCall(0).args[0],
|
||||||
|
{
|
||||||
|
selectedToken: undefined,
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert.deepEqual(
|
||||||
|
actionSpies.signTx.getCall(0).args,
|
||||||
|
[ 'mockConstructedTxParams' ]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('update()', () => {
|
||||||
|
it('should dispatch an updateTransaction action', () => {
|
||||||
|
mapDispatchToPropsObject.update({
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
editingTransactionId: 'mockEditingTransactionId',
|
||||||
|
selectedToken: 'mockSelectedToken',
|
||||||
|
unapprovedTxs: 'mockUnapprovedTxs',
|
||||||
|
})
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
utilsStubs.constructUpdatedTx.getCall(0).args[0],
|
||||||
|
{
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
editingTransactionId: 'mockEditingTransactionId',
|
||||||
|
selectedToken: 'mockSelectedToken',
|
||||||
|
unapprovedTxs: 'mockUnapprovedTxs',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert.equal(actionSpies.updateTransaction.getCall(0).args[0], 'mockConstructedUpdatedTxParams')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addToAddressBookIfNew()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.addToAddressBookIfNew('mockNewAddress', 'mockToAccounts', 'mockNickname')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.equal(utilsStubs.addressIsNew.getCall(0).args[0], 'mockToAccounts')
|
||||||
|
assert.deepEqual(
|
||||||
|
actionSpies.addToAddressBook.getCall(0).args,
|
||||||
|
[ '0xmockNewAddress', 'mockNickname' ]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,242 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
const { TOKEN_TRANSFER_FUNCTION_SIGNATURE } = require('../../send.constants')
|
||||||
|
|
||||||
|
const stubs = {
|
||||||
|
rawEncode: sinon.stub().callsFake((arr1, arr2) => {
|
||||||
|
return [ ...arr1, ...arr2 ]
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendUtils = proxyquire('../send-footer.utils.js', {
|
||||||
|
'ethereumjs-abi': {
|
||||||
|
rawEncode: stubs.rawEncode,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
addressIsNew,
|
||||||
|
formShouldBeDisabled,
|
||||||
|
constructTxParams,
|
||||||
|
constructUpdatedTx,
|
||||||
|
addHexPrefixToObjectValues,
|
||||||
|
} = sendUtils
|
||||||
|
|
||||||
|
describe('send-footer utils', () => {
|
||||||
|
|
||||||
|
describe('addHexPrefixToObjectValues()', () => {
|
||||||
|
it('should return a new object with the same properties with a 0x prefix', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
addHexPrefixToObjectValues({
|
||||||
|
prop1: '0x123',
|
||||||
|
prop2: '456',
|
||||||
|
prop3: 'x',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
prop1: '0x123',
|
||||||
|
prop2: '0x456',
|
||||||
|
prop3: '0xx',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addressIsNew()', () => {
|
||||||
|
it('should return false if the address exists in toAccounts', () => {
|
||||||
|
assert.equal(
|
||||||
|
addressIsNew([
|
||||||
|
{ address: '0xabc' },
|
||||||
|
{ address: '0xdef' },
|
||||||
|
{ address: '0xghi' },
|
||||||
|
], '0xdef'),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return true if the address does not exists in toAccounts', () => {
|
||||||
|
assert.equal(
|
||||||
|
addressIsNew([
|
||||||
|
{ address: '0xabc' },
|
||||||
|
{ address: '0xdef' },
|
||||||
|
{ address: '0xghi' },
|
||||||
|
], '0xxyz'),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('formShouldBeDisabled()', () => {
|
||||||
|
const config = {
|
||||||
|
'should return true if inError is truthy': {
|
||||||
|
inError: true,
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
'should return true if gasTotal is falsy': {
|
||||||
|
inError: false,
|
||||||
|
gasTotal: false,
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
'should return true if selectedToken is truthy and tokenBalance is falsy': {
|
||||||
|
selectedToken: true,
|
||||||
|
tokenBalance: null,
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
'should return false if inError is false and all other params are truthy': {
|
||||||
|
inError: false,
|
||||||
|
gasTotal: '0x123',
|
||||||
|
selectedToken: true,
|
||||||
|
tokenBalance: 123,
|
||||||
|
expectedResult: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Object.entries(config).map(([description, obj]) => {
|
||||||
|
it(description, () => {
|
||||||
|
assert.equal(formShouldBeDisabled(obj), obj.expectedResult)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('constructTxParams()', () => {
|
||||||
|
it('should return a new txParams object with value and to properties if there is no selectedToken', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
constructTxParams({
|
||||||
|
selectedToken: false,
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
to: '0xmockTo',
|
||||||
|
value: '0xmockAmount',
|
||||||
|
from: '0xmockFrom',
|
||||||
|
gas: '0xmockGas',
|
||||||
|
gasPrice: '0xmockGasPrice',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return a new txParams object without a to property and a 0 value if there is a selectedToken', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
constructTxParams({
|
||||||
|
selectedToken: true,
|
||||||
|
to: 'mockTo',
|
||||||
|
amount: 'mockAmount',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
value: '0x0',
|
||||||
|
from: '0xmockFrom',
|
||||||
|
gas: '0xmockGas',
|
||||||
|
gasPrice: '0xmockGasPrice',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('constructUpdatedTx()', () => {
|
||||||
|
it('should return a new object with an updated txParams', () => {
|
||||||
|
const result = constructUpdatedTx({
|
||||||
|
amount: 'mockAmount',
|
||||||
|
editingTransactionId: '0x456',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
selectedToken: false,
|
||||||
|
to: 'mockTo',
|
||||||
|
unapprovedTxs: {
|
||||||
|
'0x123': {},
|
||||||
|
'0x456': {
|
||||||
|
unapprovedTxParam: 'someOtherParam',
|
||||||
|
txParams: {
|
||||||
|
data: 'someData',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.deepEqual(result, {
|
||||||
|
unapprovedTxParam: 'someOtherParam',
|
||||||
|
txParams: {
|
||||||
|
from: '0xmockFrom',
|
||||||
|
gas: '0xmockGas',
|
||||||
|
gasPrice: '0xmockGasPrice',
|
||||||
|
value: '0xmockAmount',
|
||||||
|
to: '0xmockTo',
|
||||||
|
data: '0xsomeData',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not have data property if there is non in the original tx', () => {
|
||||||
|
const result = constructUpdatedTx({
|
||||||
|
amount: 'mockAmount',
|
||||||
|
editingTransactionId: '0x456',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
selectedToken: false,
|
||||||
|
to: 'mockTo',
|
||||||
|
unapprovedTxs: {
|
||||||
|
'0x123': {},
|
||||||
|
'0x456': {
|
||||||
|
unapprovedTxParam: 'someOtherParam',
|
||||||
|
txParams: {
|
||||||
|
from: 'oldFrom',
|
||||||
|
gas: 'oldGas',
|
||||||
|
gasPrice: 'oldGasPrice',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.deepEqual(result, {
|
||||||
|
unapprovedTxParam: 'someOtherParam',
|
||||||
|
txParams: {
|
||||||
|
from: '0xmockFrom',
|
||||||
|
gas: '0xmockGas',
|
||||||
|
gasPrice: '0xmockGasPrice',
|
||||||
|
value: '0xmockAmount',
|
||||||
|
to: '0xmockTo',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should have token property values if selectedToken is truthy', () => {
|
||||||
|
const result = constructUpdatedTx({
|
||||||
|
amount: 'mockAmount',
|
||||||
|
editingTransactionId: '0x456',
|
||||||
|
from: 'mockFrom',
|
||||||
|
gas: 'mockGas',
|
||||||
|
gasPrice: 'mockGasPrice',
|
||||||
|
selectedToken: {
|
||||||
|
address: 'mockTokenAddress',
|
||||||
|
},
|
||||||
|
to: 'mockTo',
|
||||||
|
unapprovedTxs: {
|
||||||
|
'0x123': {},
|
||||||
|
'0x456': {
|
||||||
|
unapprovedTxParam: 'someOtherParam',
|
||||||
|
txParams: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.deepEqual(result, {
|
||||||
|
unapprovedTxParam: 'someOtherParam',
|
||||||
|
txParams: {
|
||||||
|
from: '0xmockFrom',
|
||||||
|
gas: '0xmockGas',
|
||||||
|
gasPrice: '0xmockGasPrice',
|
||||||
|
value: '0x0',
|
||||||
|
to: '0xmockTokenAddress',
|
||||||
|
data: `${TOKEN_TRANSFER_FUNCTION_SIGNATURE}ss56Tont`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,55 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
clearSend: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../send-header.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'../../../actions': actionSpies,
|
||||||
|
'../../../selectors': { getSelectedToken: (s) => `mockSelectedToken:${s}` },
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send-header container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
isToken: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('clearSend()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.clearSend()
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert(actionSpies.clearSend.calledOnce)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -1,5 +1,4 @@
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import abi from 'ethereumjs-abi'
|
|
||||||
import SendEther from './send.component'
|
import SendEther from './send.component'
|
||||||
import { withRouter } from 'react-router-dom'
|
import { withRouter } from 'react-router-dom'
|
||||||
import { compose } from 'recompose'
|
import { compose } from 'recompose'
|
||||||
@ -46,7 +45,7 @@ function mapStateToProps (state) {
|
|||||||
amount: getSendAmount(state),
|
amount: getSendAmount(state),
|
||||||
amountConversionRate: getAmountConversionRate(state),
|
amountConversionRate: getAmountConversionRate(state),
|
||||||
conversionRate: getConversionRate(state),
|
conversionRate: getConversionRate(state),
|
||||||
data: generateTokenTransferData(abi, selectedAddress, selectedToken),
|
data: generateTokenTransferData(selectedAddress, selectedToken),
|
||||||
editingTransactionId: getSendEditingTransactionId(state),
|
editingTransactionId: getSendEditingTransactionId(state),
|
||||||
from: getSendFromObject(state),
|
from: getSendFromObject(state),
|
||||||
gasLimit: getGasLimit(state),
|
gasLimit: getGasLimit(state),
|
||||||
@ -72,6 +71,7 @@ function mapDispatchToProps (dispatch) {
|
|||||||
selectedAddress,
|
selectedAddress,
|
||||||
selectedToken,
|
selectedToken,
|
||||||
}) => {
|
}) => {
|
||||||
|
console.log(`editingTransactionId`, editingTransactionId)
|
||||||
!editingTransactionId
|
!editingTransactionId
|
||||||
? dispatch(updateGasTotal({ selectedAddress, selectedToken, data }))
|
? dispatch(updateGasTotal({ selectedAddress, selectedToken, data }))
|
||||||
: dispatch(setGasTotal(calcGasTotal(gasLimit, gasPrice)))
|
: dispatch(setGasTotal(calcGasTotal(gasLimit, gasPrice)))
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { valuesFor } from '../../util'
|
const { valuesFor } = require('../../util')
|
||||||
import abi from 'human-standard-token-abi'
|
const abi = require('human-standard-token-abi')
|
||||||
import {
|
const {
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
} from '../../conversion-util'
|
} = require('../../conversion-util')
|
||||||
|
|
||||||
const selectors = {
|
const selectors = {
|
||||||
accountsWithSendEtherInfoSelector,
|
accountsWithSendEtherInfoSelector,
|
||||||
autoAddToBetaUI,
|
// autoAddToBetaUI,
|
||||||
getAddressBook,
|
getAddressBook,
|
||||||
getAmountConversionRate,
|
getAmountConversionRate,
|
||||||
getConversionRate,
|
getConversionRate,
|
||||||
@ -58,22 +58,22 @@ function accountsWithSendEtherInfoSelector (state) {
|
|||||||
return accountsWithSendEtherInfo
|
return accountsWithSendEtherInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoAddToBetaUI (state) {
|
// function autoAddToBetaUI (state) {
|
||||||
const autoAddTransactionThreshold = 12
|
// const autoAddTransactionThreshold = 12
|
||||||
const autoAddAccountsThreshold = 2
|
// const autoAddAccountsThreshold = 2
|
||||||
const autoAddTokensThreshold = 1
|
// const autoAddTokensThreshold = 1
|
||||||
|
|
||||||
const numberOfTransactions = state.metamask.selectedAddressTxList.length
|
// const numberOfTransactions = state.metamask.selectedAddressTxList.length
|
||||||
const numberOfAccounts = Object.keys(state.metamask.accounts).length
|
// const numberOfAccounts = Object.keys(state.metamask.accounts).length
|
||||||
const numberOfTokensAdded = state.metamask.tokens.length
|
// const numberOfTokensAdded = state.metamask.tokens.length
|
||||||
|
|
||||||
const userPassesThreshold = (numberOfTransactions > autoAddTransactionThreshold) &&
|
// const userPassesThreshold = (numberOfTransactions > autoAddTransactionThreshold) &&
|
||||||
(numberOfAccounts > autoAddAccountsThreshold) &&
|
// (numberOfAccounts > autoAddAccountsThreshold) &&
|
||||||
(numberOfTokensAdded > autoAddTokensThreshold)
|
// (numberOfTokensAdded > autoAddTokensThreshold)
|
||||||
const userIsNotInBeta = !state.metamask.featureFlags.betaUI
|
// const userIsNotInBeta = !state.metamask.featureFlags.betaUI
|
||||||
|
|
||||||
return userIsNotInBeta && userPassesThreshold
|
// return userIsNotInBeta && userPassesThreshold
|
||||||
}
|
// }
|
||||||
|
|
||||||
function getAddressBook (state) {
|
function getAddressBook (state) {
|
||||||
return state.metamask.addressBook
|
return state.metamask.addressBook
|
||||||
@ -117,14 +117,14 @@ function getForceGasMin (state) {
|
|||||||
return state.metamask.send.forceGasMin
|
return state.metamask.send.forceGasMin
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGasPrice (state) {
|
|
||||||
return state.metamask.send.gasPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
function getGasLimit (state) {
|
function getGasLimit (state) {
|
||||||
return state.metamask.send.gasLimit
|
return state.metamask.send.gasLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGasPrice (state) {
|
||||||
|
return state.metamask.send.gasPrice
|
||||||
|
}
|
||||||
|
|
||||||
function getGasTotal (state) {
|
function getGasTotal (state) {
|
||||||
return state.metamask.send.gasTotal
|
return state.metamask.send.gasTotal
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,17 @@ const {
|
|||||||
conversionUtil,
|
conversionUtil,
|
||||||
conversionGTE,
|
conversionGTE,
|
||||||
multiplyCurrencies,
|
multiplyCurrencies,
|
||||||
|
conversionGreaterThan,
|
||||||
} = require('../../conversion-util')
|
} = require('../../conversion-util')
|
||||||
const {
|
const {
|
||||||
calcTokenAmount,
|
calcTokenAmount,
|
||||||
} = require('../../token-util')
|
} = require('../../token-util')
|
||||||
const {
|
|
||||||
conversionGreaterThan,
|
|
||||||
} = require('../../conversion-util')
|
|
||||||
const {
|
const {
|
||||||
INSUFFICIENT_FUNDS_ERROR,
|
INSUFFICIENT_FUNDS_ERROR,
|
||||||
INSUFFICIENT_TOKENS_ERROR,
|
INSUFFICIENT_TOKENS_ERROR,
|
||||||
NEGATIVE_ETH_ERROR,
|
NEGATIVE_ETH_ERROR,
|
||||||
} = require('./send.constants')
|
} = require('./send.constants')
|
||||||
|
const abi = require('ethereumjs-abi')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
calcGasTotal,
|
calcGasTotal,
|
||||||
@ -179,8 +178,9 @@ function doesAmountErrorRequireUpdate ({
|
|||||||
return amountErrorRequiresUpdate
|
return amountErrorRequiresUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateTokenTransferData (abi, selectedAddress, selectedToken) {
|
function generateTokenTransferData (selectedAddress, selectedToken) {
|
||||||
if (!selectedToken) return
|
if (!selectedToken) return
|
||||||
|
console.log(`abi.rawEncode`, abi.rawEncode)
|
||||||
return Array.prototype.map.call(
|
return Array.prototype.map.call(
|
||||||
abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
|
abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
|
||||||
x => ('00' + x.toString(16)).slice(-2)
|
x => ('00' + x.toString(16)).slice(-2)
|
||||||
|
@ -0,0 +1,150 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
|
||||||
|
let mapStateToProps
|
||||||
|
let mapDispatchToProps
|
||||||
|
|
||||||
|
const actionSpies = {
|
||||||
|
updateSendTokenBalance: sinon.spy(),
|
||||||
|
updateGasTotal: sinon.spy(),
|
||||||
|
setGasTotal: sinon.spy(),
|
||||||
|
}
|
||||||
|
const duckActionSpies = {
|
||||||
|
updateSendErrors: sinon.spy(),
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyquire('../send.container.js', {
|
||||||
|
'react-redux': {
|
||||||
|
connect: (ms, md) => {
|
||||||
|
mapStateToProps = ms
|
||||||
|
mapDispatchToProps = md
|
||||||
|
return () => ({})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'react-router-dom': { withRouter: () => {} },
|
||||||
|
'recompose': { compose: (arg1, arg2) => () => arg2() },
|
||||||
|
'./send.selectors': {
|
||||||
|
getAmountConversionRate: (s) => `mockAmountConversionRate:${s}`,
|
||||||
|
getConversionRate: (s) => `mockConversionRate:${s}`,
|
||||||
|
getCurrentNetwork: (s) => `mockNetwork:${s}`,
|
||||||
|
getGasLimit: (s) => `mockGasLimit:${s}`,
|
||||||
|
getGasPrice: (s) => `mockGasPrice:${s}`,
|
||||||
|
getGasTotal: (s) => `mockGasTotal:${s}`,
|
||||||
|
getPrimaryCurrency: (s) => `mockPrimaryCurrency:${s}`,
|
||||||
|
getSelectedAddress: (s) => `mockSelectedAddress:${s}`,
|
||||||
|
getSelectedToken: (s) => `mockSelectedToken:${s}`,
|
||||||
|
getSelectedTokenContract: (s) => `mockTokenContract:${s}`,
|
||||||
|
getSelectedTokenToFiatRate: (s) => `mockTokenToFiatRate:${s}`,
|
||||||
|
getSendAmount: (s) => `mockAmount:${s}`,
|
||||||
|
getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`,
|
||||||
|
getSendFromObject: (s) => `mockFrom:${s}`,
|
||||||
|
getTokenBalance: (s) => `mockTokenBalance:${s}`,
|
||||||
|
},
|
||||||
|
'../../actions': actionSpies,
|
||||||
|
'../../ducks/send': duckActionSpies,
|
||||||
|
'./send.utils.js': {
|
||||||
|
calcGasTotal: (gasLimit, gasPrice) => gasLimit + gasPrice,
|
||||||
|
generateTokenTransferData: (a, b) => `mockData:${a + b}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('send container', () => {
|
||||||
|
|
||||||
|
describe('mapStateToProps()', () => {
|
||||||
|
|
||||||
|
it('should map the correct properties to props', () => {
|
||||||
|
assert.deepEqual(mapStateToProps('mockState'), {
|
||||||
|
amount: 'mockAmount:mockState',
|
||||||
|
amountConversionRate: 'mockAmountConversionRate:mockState',
|
||||||
|
conversionRate: 'mockConversionRate:mockState',
|
||||||
|
data: 'mockData:mockSelectedAddress:mockStatemockSelectedToken:mockState',
|
||||||
|
editingTransactionId: 'mockEditingTransactionId:mockState',
|
||||||
|
from: 'mockFrom:mockState',
|
||||||
|
gasLimit: 'mockGasLimit:mockState',
|
||||||
|
gasPrice: 'mockGasPrice:mockState',
|
||||||
|
gasTotal: 'mockGasTotal:mockState',
|
||||||
|
network: 'mockNetwork:mockState',
|
||||||
|
primaryCurrency: 'mockPrimaryCurrency:mockState',
|
||||||
|
selectedAddress: 'mockSelectedAddress:mockState',
|
||||||
|
selectedToken: 'mockSelectedToken:mockState',
|
||||||
|
tokenBalance: 'mockTokenBalance:mockState',
|
||||||
|
tokenContract: 'mockTokenContract:mockState',
|
||||||
|
tokenToFiatRate: 'mockTokenToFiatRate:mockState',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mapDispatchToProps()', () => {
|
||||||
|
let dispatchSpy
|
||||||
|
let mapDispatchToPropsObject
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dispatchSpy = sinon.spy()
|
||||||
|
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateAndSetGasTotal()', () => {
|
||||||
|
const mockProps = {
|
||||||
|
data: '0x1',
|
||||||
|
editingTransactionId: '0x2',
|
||||||
|
gasLimit: '0x3',
|
||||||
|
gasPrice: '0x4',
|
||||||
|
selectedAddress: '0x4',
|
||||||
|
selectedToken: { address: '0x1' },
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should dispatch a setGasTotal action when editingTransactionId is truthy', () => {
|
||||||
|
mapDispatchToPropsObject.updateAndSetGasTotal(mockProps)
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
actionSpies.setGasTotal.getCall(0).args[0],
|
||||||
|
'0x30x4'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should dispatch an updateGasTotal action when editingTransactionId is falsy', () => {
|
||||||
|
const { selectedAddress, selectedToken, data } = mockProps
|
||||||
|
mapDispatchToPropsObject.updateAndSetGasTotal(
|
||||||
|
Object.assign(mockProps, {editingTransactionId: false})
|
||||||
|
)
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
actionSpies.updateGasTotal.getCall(0).args[0],
|
||||||
|
{ selectedAddress, selectedToken, data }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateSendTokenBalance()', () => {
|
||||||
|
const mockProps = {
|
||||||
|
address: '0x10',
|
||||||
|
tokenContract: '0x00a',
|
||||||
|
selectedToken: {address: '0x1'},
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.updateSendTokenBalance(Object.assign({}, mockProps))
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.deepEqual(
|
||||||
|
actionSpies.updateSendTokenBalance.getCall(0).args[0],
|
||||||
|
mockProps
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('updateSendErrors()', () => {
|
||||||
|
it('should dispatch an action', () => {
|
||||||
|
mapDispatchToPropsObject.updateSendErrors('mockError')
|
||||||
|
assert(dispatchSpy.calledOnce)
|
||||||
|
assert.equal(
|
||||||
|
duckActionSpies.updateSendErrors.getCall(0).args[0],
|
||||||
|
'mockError'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
191
ui/app/components/send_/tests/send-selectors-test-data.js
Normal file
191
ui/app/components/send_/tests/send-selectors-test-data.js
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
module.exports = {
|
||||||
|
'metamask': {
|
||||||
|
'isInitialized': true,
|
||||||
|
'isUnlocked': true,
|
||||||
|
'featureFlags': {'betaUI': true},
|
||||||
|
'rpcTarget': 'https://rawtestrpc.metamask.io/',
|
||||||
|
'identities': {
|
||||||
|
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': {
|
||||||
|
'address': '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825',
|
||||||
|
'name': 'Send Account 1',
|
||||||
|
},
|
||||||
|
'0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb': {
|
||||||
|
'address': '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
|
||||||
|
'name': 'Send Account 2',
|
||||||
|
},
|
||||||
|
'0x2f8d4a878cfa04a6e60d46362f5644deab66572d': {
|
||||||
|
'address': '0x2f8d4a878cfa04a6e60d46362f5644deab66572d',
|
||||||
|
'name': 'Send Account 3',
|
||||||
|
},
|
||||||
|
'0xd85a4b6a394794842887b8284293d69163007bbb': {
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
'name': 'Send Account 4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'currentCurrency': 'USD',
|
||||||
|
'conversionRate': 1200.88200327,
|
||||||
|
'conversionDate': 1489013762,
|
||||||
|
'noActiveNotices': true,
|
||||||
|
'frequentRpcList': [],
|
||||||
|
'network': '3',
|
||||||
|
'accounts': {
|
||||||
|
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': {
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x47c9d71831c76efe',
|
||||||
|
'nonce': '0x1b',
|
||||||
|
'address': '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825',
|
||||||
|
},
|
||||||
|
'0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb': {
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x37452b1315889f80',
|
||||||
|
'nonce': '0xa',
|
||||||
|
'address': '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
|
||||||
|
},
|
||||||
|
'0x2f8d4a878cfa04a6e60d46362f5644deab66572d': {
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x30c9d71831c76efe',
|
||||||
|
'nonce': '0x1c',
|
||||||
|
'address': '0x2f8d4a878cfa04a6e60d46362f5644deab66572d',
|
||||||
|
},
|
||||||
|
'0xd85a4b6a394794842887b8284293d69163007bbb': {
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x0',
|
||||||
|
'nonce': '0x0',
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'addressBook': [
|
||||||
|
{
|
||||||
|
'address': '0x06195827297c7a80a443b6894d3bdb8824b43896',
|
||||||
|
'name': 'Address Book Account 1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'tokens': [
|
||||||
|
{
|
||||||
|
'address': '0x1a195821297c7a80a433b6894d3bdb8824b43896',
|
||||||
|
'decimals': 18,
|
||||||
|
'symbol': 'ABC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'address': '0x8d6b81208414189a58339873ab429b6c47ab92d3',
|
||||||
|
'decimals': 4,
|
||||||
|
'symbol': 'DEF',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'address': '0xa42084c8d1d9a2198631988579bb36b48433a72b',
|
||||||
|
'decimals': 18,
|
||||||
|
'symbol': 'GHI',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'tokenExchangeRates': {
|
||||||
|
'def_eth': {
|
||||||
|
rate: 2.0,
|
||||||
|
},
|
||||||
|
'ghi_eth': {
|
||||||
|
rate: 31.01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'transactions': {},
|
||||||
|
'selectedAddressTxList': [],
|
||||||
|
'selectedTokenAddress': '0x8d6b81208414189a58339873ab429b6c47ab92d3',
|
||||||
|
'unapprovedMsgs': {},
|
||||||
|
'unapprovedMsgCount': 0,
|
||||||
|
'unapprovedPersonalMsgs': {},
|
||||||
|
'unapprovedPersonalMsgCount': 0,
|
||||||
|
'keyringTypes': [
|
||||||
|
'Simple Key Pair',
|
||||||
|
'HD Key Tree',
|
||||||
|
],
|
||||||
|
'keyrings': [
|
||||||
|
{
|
||||||
|
'type': 'HD Key Tree',
|
||||||
|
'accounts': [
|
||||||
|
'fdea65c8e26263f6d9a1b5de9555d2931a33b825',
|
||||||
|
'c5b8dbac4c1d3f152cdeb400e2313f309c410acb',
|
||||||
|
'2f8d4a878cfa04a6e60d46362f5644deab66572d',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'type': 'Simple Key Pair',
|
||||||
|
'accounts': [
|
||||||
|
'0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'selectedAddress': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
'provider': {
|
||||||
|
'type': 'testnet',
|
||||||
|
},
|
||||||
|
'shapeShiftTxList': [],
|
||||||
|
'lostAccounts': [],
|
||||||
|
'send': {
|
||||||
|
'gasLimit': '0xFFFF',
|
||||||
|
'gasPrice': '0xaa',
|
||||||
|
'gasTotal': '0xb451dc41b578',
|
||||||
|
'tokenBalance': 3434,
|
||||||
|
'from': {
|
||||||
|
'address': '0xabcdefg',
|
||||||
|
'balance': '0x5f4e3d2c1',
|
||||||
|
},
|
||||||
|
'to': '0x987fedabc',
|
||||||
|
'amount': '0x080',
|
||||||
|
'memo': '',
|
||||||
|
'errors': {
|
||||||
|
'someError': null,
|
||||||
|
},
|
||||||
|
'maxModeOn': false,
|
||||||
|
'editingTransactionId': 97531,
|
||||||
|
'forceGasMin': true,
|
||||||
|
},
|
||||||
|
'unapprovedTxs': {
|
||||||
|
'4768706228115573': {
|
||||||
|
'id': 4768706228115573,
|
||||||
|
'time': 1487363153561,
|
||||||
|
'status': 'unapproved',
|
||||||
|
'gasMultiplier': 1,
|
||||||
|
'metamaskNetworkId': '3',
|
||||||
|
'txParams': {
|
||||||
|
'from': '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
|
||||||
|
'to': '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761',
|
||||||
|
'value': '0xde0b6b3a7640000',
|
||||||
|
'metamaskId': 4768706228115573,
|
||||||
|
'metamaskNetworkId': '3',
|
||||||
|
'gas': '0x5209',
|
||||||
|
},
|
||||||
|
'gasLimitSpecified': false,
|
||||||
|
'estimatedGas': '0x5209',
|
||||||
|
'txFee': '17e0186e60800',
|
||||||
|
'txValue': 'de0b6b3a7640000',
|
||||||
|
'maxCost': 'de234b52e4a0800',
|
||||||
|
'gasPrice': '4a817c800',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'currentLocale': 'en',
|
||||||
|
},
|
||||||
|
'appState': {
|
||||||
|
'menuOpen': false,
|
||||||
|
'currentView': {
|
||||||
|
'name': 'accountDetail',
|
||||||
|
'detailView': null,
|
||||||
|
'context': '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
|
||||||
|
},
|
||||||
|
'accountDetail': {
|
||||||
|
'subview': 'transactions',
|
||||||
|
},
|
||||||
|
'modal': {
|
||||||
|
'modalState': {},
|
||||||
|
'previousModalState': {},
|
||||||
|
},
|
||||||
|
'transForward': true,
|
||||||
|
'isLoading': false,
|
||||||
|
'warning': null,
|
||||||
|
'scrollToBottom': false,
|
||||||
|
'forgottenPassword': null,
|
||||||
|
},
|
||||||
|
'identities': {},
|
||||||
|
'send': {
|
||||||
|
'fromDropdownOpen': false,
|
||||||
|
'toDropdownOpen': false,
|
||||||
|
'errors': { 'someError': null },
|
||||||
|
},
|
||||||
|
}
|
@ -0,0 +1,572 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import selectors from '../send.selectors.js'
|
||||||
|
const {
|
||||||
|
accountsWithSendEtherInfoSelector,
|
||||||
|
// autoAddToBetaUI,
|
||||||
|
getAddressBook,
|
||||||
|
getAmountConversionRate,
|
||||||
|
getConversionRate,
|
||||||
|
getConvertedCurrency,
|
||||||
|
getCurrentAccountWithSendEtherInfo,
|
||||||
|
getCurrentCurrency,
|
||||||
|
getCurrentNetwork,
|
||||||
|
getCurrentViewContext,
|
||||||
|
getForceGasMin,
|
||||||
|
getGasLimit,
|
||||||
|
getGasPrice,
|
||||||
|
getGasTotal,
|
||||||
|
getPrimaryCurrency,
|
||||||
|
getSelectedAccount,
|
||||||
|
getSelectedAddress,
|
||||||
|
getSelectedIdentity,
|
||||||
|
getSelectedToken,
|
||||||
|
// getSelectedTokenContract,
|
||||||
|
getSelectedTokenExchangeRate,
|
||||||
|
getSelectedTokenToFiatRate,
|
||||||
|
getSendAmount,
|
||||||
|
getSendEditingTransactionId,
|
||||||
|
getSendErrors,
|
||||||
|
getSendFrom,
|
||||||
|
getSendFromBalance,
|
||||||
|
getSendFromObject,
|
||||||
|
getSendMaxModeState,
|
||||||
|
getSendTo,
|
||||||
|
getSendToAccounts,
|
||||||
|
getTokenBalance,
|
||||||
|
getTokenExchangeRate,
|
||||||
|
getUnapprovedTxs,
|
||||||
|
isSendFormInError,
|
||||||
|
// transactionsSelector,
|
||||||
|
} = selectors
|
||||||
|
import mockState from './send-selectors-test-data'
|
||||||
|
|
||||||
|
describe('send selectors', () => {
|
||||||
|
|
||||||
|
describe('accountsWithSendEtherInfoSelector()', () => {
|
||||||
|
it('should return an array of account objects with name info from identities', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
accountsWithSendEtherInfoSelector(mockState),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x47c9d71831c76efe',
|
||||||
|
'nonce': '0x1b',
|
||||||
|
'address': '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825',
|
||||||
|
'name': 'Send Account 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x37452b1315889f80',
|
||||||
|
'nonce': '0xa',
|
||||||
|
'address': '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
|
||||||
|
'name': 'Send Account 2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x30c9d71831c76efe',
|
||||||
|
'nonce': '0x1c',
|
||||||
|
'address': '0x2f8d4a878cfa04a6e60d46362f5644deab66572d',
|
||||||
|
'name': 'Send Account 3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x0',
|
||||||
|
'nonce': '0x0',
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
'name': 'Send Account 4',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// describe('autoAddToBetaUI()', () => {
|
||||||
|
// it('should', () => {
|
||||||
|
// assert.deepEqual(
|
||||||
|
// autoAddToBetaUI(mockState),
|
||||||
|
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
|
||||||
|
describe('getAddressBook()', () => {
|
||||||
|
it('should return the address book', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getAddressBook(mockState),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'address': '0x06195827297c7a80a443b6894d3bdb8824b43896',
|
||||||
|
'name': 'Address Book Account 1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getAmountConversionRate()', () => {
|
||||||
|
it('should return the token conversion rate if a token is selected', () => {
|
||||||
|
assert.equal(
|
||||||
|
getAmountConversionRate(mockState),
|
||||||
|
2401.76400654
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the eth conversion rate if no token is selected', () => {
|
||||||
|
const editedMockState = {
|
||||||
|
metamask: Object.assign({}, mockState.metamask, { selectedTokenAddress: null }),
|
||||||
|
}
|
||||||
|
assert.equal(
|
||||||
|
getAmountConversionRate(editedMockState),
|
||||||
|
1200.88200327
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getConversionRate()', () => {
|
||||||
|
it('should return the eth conversion rate', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getConversionRate(mockState),
|
||||||
|
1200.88200327
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getConvertedCurrency()', () => {
|
||||||
|
it('should return the currently selected currency', () => {
|
||||||
|
assert.equal(
|
||||||
|
getConvertedCurrency(mockState),
|
||||||
|
'USD'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getCurrentAccountWithSendEtherInfo()', () => {
|
||||||
|
it('should return the currently selected account with identity info', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getCurrentAccountWithSendEtherInfo(mockState),
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x0',
|
||||||
|
'nonce': '0x0',
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
'name': 'Send Account 4',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getCurrentCurrency()', () => {
|
||||||
|
it('should return the currently selected currency', () => {
|
||||||
|
assert.equal(
|
||||||
|
getCurrentCurrency(mockState),
|
||||||
|
'USD'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getCurrentNetwork()', () => {
|
||||||
|
it('should return the id of the currently selected network', () => {
|
||||||
|
assert.equal(
|
||||||
|
getCurrentNetwork(mockState),
|
||||||
|
'3'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getCurrentViewContext()', () => {
|
||||||
|
it('should return the context of the current view', () => {
|
||||||
|
assert.equal(
|
||||||
|
getCurrentViewContext(mockState),
|
||||||
|
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getForceGasMin()', () => {
|
||||||
|
it('should get the send.forceGasMin property', () => {
|
||||||
|
assert.equal(
|
||||||
|
getForceGasMin(mockState),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getGasLimit()', () => {
|
||||||
|
it('should return the send.gasLimit', () => {
|
||||||
|
assert.equal(
|
||||||
|
getGasLimit(mockState),
|
||||||
|
'0xFFFF'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getGasPrice()', () => {
|
||||||
|
it('should return the send.gasPrice', () => {
|
||||||
|
assert.equal(
|
||||||
|
getGasPrice(mockState),
|
||||||
|
'0xaa'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getGasTotal()', () => {
|
||||||
|
it('should return the send.gasTotal', () => {
|
||||||
|
assert.equal(
|
||||||
|
getGasTotal(mockState),
|
||||||
|
'0xb451dc41b578'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getPrimaryCurrency()', () => {
|
||||||
|
it('should return the symbol of the selected token', () => {
|
||||||
|
assert.equal(
|
||||||
|
getPrimaryCurrency(mockState),
|
||||||
|
'DEF'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSelectedAccount()', () => {
|
||||||
|
it('should return the currently selected account', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getSelectedAccount(mockState),
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x0',
|
||||||
|
'nonce': '0x0',
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSelectedAddress()', () => {
|
||||||
|
it('should', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSelectedAddress(mockState),
|
||||||
|
'0xd85a4b6a394794842887b8284293d69163007bbb'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSelectedIdentity()', () => {
|
||||||
|
it('should return the identity object of the currently selected address', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getSelectedIdentity(mockState),
|
||||||
|
{
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
'name': 'Send Account 4',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSelectedToken()', () => {
|
||||||
|
it('should return the currently selected token if selected', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getSelectedToken(mockState),
|
||||||
|
{
|
||||||
|
'address': '0x8d6b81208414189a58339873ab429b6c47ab92d3',
|
||||||
|
'decimals': 4,
|
||||||
|
'symbol': 'DEF',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the send token if none is currently selected, but a send token exists', () => {
|
||||||
|
const mockSendToken = {
|
||||||
|
'address': '0x123456708414189a58339873ab429b6c47ab92d3',
|
||||||
|
'decimals': 4,
|
||||||
|
'symbol': 'JKL',
|
||||||
|
}
|
||||||
|
const editedMockState = {
|
||||||
|
metamask: Object.assign({}, mockState.metamask, {
|
||||||
|
selectedTokenAddress: null,
|
||||||
|
send: {
|
||||||
|
token: mockSendToken,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
getSelectedToken(editedMockState),
|
||||||
|
Object.assign({}, mockSendToken)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// describe('getSelectedTokenContract()', () => {
|
||||||
|
// it('should', () => {
|
||||||
|
// assert.deepEqual(
|
||||||
|
// getSelectedTokenContract(mockState),
|
||||||
|
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
|
||||||
|
describe('getSelectedTokenExchangeRate()', () => {
|
||||||
|
it('should return the exchange rate for the selected token', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSelectedTokenExchangeRate(mockState),
|
||||||
|
2.0
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSelectedTokenToFiatRate()', () => {
|
||||||
|
it('should return rate for converting the selected token to fiat', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSelectedTokenToFiatRate(mockState),
|
||||||
|
2401.76400654
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendAmount()', () => {
|
||||||
|
it('should return the send.amount', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSendAmount(mockState),
|
||||||
|
'0x080'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendEditingTransactionId()', () => {
|
||||||
|
it('should return the send.editingTransactionId', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSendEditingTransactionId(mockState),
|
||||||
|
97531
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendErrors()', () => {
|
||||||
|
it('should return the send.errors', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getSendErrors(mockState),
|
||||||
|
{ 'someError': null }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendFrom()', () => {
|
||||||
|
it('should return the send.from', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getSendFrom(mockState),
|
||||||
|
{
|
||||||
|
'address': '0xabcdefg',
|
||||||
|
'balance': '0x5f4e3d2c1',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendFromBalance()', () => {
|
||||||
|
it('should get the send.from balance if it exists', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSendFromBalance(mockState),
|
||||||
|
'0x5f4e3d2c1'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get the selected account balance if the send.from does not exist', () => {
|
||||||
|
const editedMockState = {
|
||||||
|
metamask: Object.assign({}, mockState.metamask, {
|
||||||
|
send: {
|
||||||
|
from: null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
assert.equal(
|
||||||
|
getSendFromBalance(editedMockState),
|
||||||
|
'0x0'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendFromObject()', () => {
|
||||||
|
it('should return send.from if it exists', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getSendFromObject(mockState),
|
||||||
|
{
|
||||||
|
'address': '0xabcdefg',
|
||||||
|
'balance': '0x5f4e3d2c1',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the current account with send ether info if send.from does not exist', () => {
|
||||||
|
const editedMockState = {
|
||||||
|
metamask: Object.assign({}, mockState.metamask, {
|
||||||
|
send: {
|
||||||
|
from: null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
getSendFromObject(editedMockState),
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x0',
|
||||||
|
'nonce': '0x0',
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
'name': 'Send Account 4',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendMaxModeState()', () => {
|
||||||
|
it('should return send.maxModeOn', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSendMaxModeState(mockState),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendTo()', () => {
|
||||||
|
it('should return send.to', () => {
|
||||||
|
assert.equal(
|
||||||
|
getSendTo(mockState),
|
||||||
|
'0x987fedabc'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getSendToAccounts()', () => {
|
||||||
|
it('should return an array including all the users accounts and the address book', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getSendToAccounts(mockState),
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x47c9d71831c76efe',
|
||||||
|
'nonce': '0x1b',
|
||||||
|
'address': '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825',
|
||||||
|
'name': 'Send Account 1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x37452b1315889f80',
|
||||||
|
'nonce': '0xa',
|
||||||
|
'address': '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
|
||||||
|
'name': 'Send Account 2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x30c9d71831c76efe',
|
||||||
|
'nonce': '0x1c',
|
||||||
|
'address': '0x2f8d4a878cfa04a6e60d46362f5644deab66572d',
|
||||||
|
'name': 'Send Account 3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'code': '0x',
|
||||||
|
'balance': '0x0',
|
||||||
|
'nonce': '0x0',
|
||||||
|
'address': '0xd85a4b6a394794842887b8284293d69163007bbb',
|
||||||
|
'name': 'Send Account 4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'address': '0x06195827297c7a80a443b6894d3bdb8824b43896',
|
||||||
|
'name': 'Address Book Account 1',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getTokenBalance()', () => {
|
||||||
|
it('should', () => {
|
||||||
|
assert.equal(
|
||||||
|
getTokenBalance(mockState),
|
||||||
|
3434
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getTokenExchangeRate()', () => {
|
||||||
|
it('should return the passed tokens exchange rates', () => {
|
||||||
|
assert.equal(
|
||||||
|
getTokenExchangeRate(mockState, 'GHI'),
|
||||||
|
31.01
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getUnapprovedTxs()', () => {
|
||||||
|
it('should return the unapproved txs', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getUnapprovedTxs(mockState),
|
||||||
|
{
|
||||||
|
'4768706228115573': {
|
||||||
|
'id': 4768706228115573,
|
||||||
|
'time': 1487363153561,
|
||||||
|
'status': 'unapproved',
|
||||||
|
'gasMultiplier': 1,
|
||||||
|
'metamaskNetworkId': '3',
|
||||||
|
'txParams': {
|
||||||
|
'from': '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
|
||||||
|
'to': '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761',
|
||||||
|
'value': '0xde0b6b3a7640000',
|
||||||
|
'metamaskId': 4768706228115573,
|
||||||
|
'metamaskNetworkId': '3',
|
||||||
|
'gas': '0x5209',
|
||||||
|
},
|
||||||
|
'gasLimitSpecified': false,
|
||||||
|
'estimatedGas': '0x5209',
|
||||||
|
'txFee': '17e0186e60800',
|
||||||
|
'txValue': 'de0b6b3a7640000',
|
||||||
|
'maxCost': 'de234b52e4a0800',
|
||||||
|
'gasPrice': '4a817c800',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isSendFormInError()', () => {
|
||||||
|
it('should return true if amount or to errors are truthy', () => {
|
||||||
|
const editedMockState1 = {
|
||||||
|
send: Object.assign({}, mockState.send, {
|
||||||
|
errors: { amount: true },
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
isSendFormInError(editedMockState1),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
const editedMockState2 = {
|
||||||
|
send: Object.assign({}, mockState.send, {
|
||||||
|
errors: { to: true },
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
isSendFormInError(editedMockState2),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false if amount is falsy and to is null', () => {
|
||||||
|
const editedMockState = {
|
||||||
|
send: Object.assign({}, mockState.send, { errors: { amount: false, to: null } }),
|
||||||
|
}
|
||||||
|
assert.deepEqual(
|
||||||
|
isSendFormInError(editedMockState),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// describe('transactionsSelector()', () => {
|
||||||
|
// it('should', () => {
|
||||||
|
// assert.deepEqual(
|
||||||
|
// transactionsSelector(mockState),
|
||||||
|
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,264 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
import sinon from 'sinon'
|
||||||
|
import proxyquire from 'proxyquire'
|
||||||
|
|
||||||
|
const {
|
||||||
|
INSUFFICIENT_FUNDS_ERROR,
|
||||||
|
INSUFFICIENT_TOKENS_ERROR,
|
||||||
|
} = require('../send.constants')
|
||||||
|
|
||||||
|
const stubs = {
|
||||||
|
addCurrencies: sinon.stub().callsFake((a, b, obj) => a + b),
|
||||||
|
conversionUtil: sinon.stub().callsFake((val, obj) => parseInt(val, 16)),
|
||||||
|
conversionGTE: sinon.stub().callsFake((obj1, obj2) => obj1.value > obj2.value),
|
||||||
|
multiplyCurrencies: sinon.stub().callsFake((a, b) => a * b),
|
||||||
|
calcTokenAmount: sinon.stub().callsFake((a, d) => 'calc:' + a + d),
|
||||||
|
rawEncode: sinon.stub().returns([16, 1100]),
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendUtils = proxyquire('../send.utils.js', {
|
||||||
|
'../../conversion-util': {
|
||||||
|
addCurrencies: stubs.addCurrencies,
|
||||||
|
conversionUtil: stubs.conversionUtil,
|
||||||
|
conversionGTE: stubs.conversionGTE,
|
||||||
|
multiplyCurrencies: stubs.multiplyCurrencies,
|
||||||
|
},
|
||||||
|
'../../token-util': { calcTokenAmount: stubs.calcTokenAmount },
|
||||||
|
'ethereumjs-abi': {
|
||||||
|
rawEncode: stubs.rawEncode,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
calcGasTotal,
|
||||||
|
doesAmountErrorRequireUpdate,
|
||||||
|
generateTokenTransferData,
|
||||||
|
getAmountErrorObject,
|
||||||
|
getParamsForGasEstimate,
|
||||||
|
calcTokenBalance,
|
||||||
|
isBalanceSufficient,
|
||||||
|
isTokenBalanceSufficient,
|
||||||
|
} = sendUtils
|
||||||
|
|
||||||
|
describe('send utils', () => {
|
||||||
|
|
||||||
|
describe('calcGasTotal()', () => {
|
||||||
|
it('should call multiplyCurrencies with the correct params and return the multiplyCurrencies return', () => {
|
||||||
|
const result = calcGasTotal(12, 15)
|
||||||
|
assert.equal(result, 180)
|
||||||
|
const call_ = stubs.multiplyCurrencies.getCall(0).args
|
||||||
|
assert.deepEqual(
|
||||||
|
call_,
|
||||||
|
[12, 15, {
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
multiplicandBase: 16,
|
||||||
|
multiplierBase: 16,
|
||||||
|
} ]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('doesAmountErrorRequireUpdate()', () => {
|
||||||
|
const config = {
|
||||||
|
'should return true if balances are different': {
|
||||||
|
balance: 0,
|
||||||
|
prevBalance: 1,
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
'should return true if gasTotals are different': {
|
||||||
|
gasTotal: 0,
|
||||||
|
prevGasTotal: 1,
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
'should return true if token balances are different': {
|
||||||
|
tokenBalance: 0,
|
||||||
|
prevTokenBalance: 1,
|
||||||
|
selectedToken: 'someToken',
|
||||||
|
expectedResult: true,
|
||||||
|
},
|
||||||
|
'should return false if they are all the same': {
|
||||||
|
balance: 1,
|
||||||
|
prevBalance: 1,
|
||||||
|
gasTotal: 1,
|
||||||
|
prevGasTotal: 1,
|
||||||
|
tokenBalance: 1,
|
||||||
|
prevTokenBalance: 1,
|
||||||
|
selectedToken: 'someToken',
|
||||||
|
expectedResult: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Object.entries(config).map(([description, obj]) => {
|
||||||
|
it(description, () => {
|
||||||
|
assert.equal(doesAmountErrorRequireUpdate(obj), obj.expectedResult)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('generateTokenTransferData()', () => {
|
||||||
|
it('should return undefined if not passed a selected token', () => {
|
||||||
|
assert.equal(generateTokenTransferData('mockAddress', false), undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return encoded token transfer data', () => {
|
||||||
|
assert.equal(generateTokenTransferData('mockAddress', true), '104c')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getAmountErrorObject()', () => {
|
||||||
|
const config = {
|
||||||
|
'should return insufficientFunds error if isBalanceSufficient returns false': {
|
||||||
|
amount: 15,
|
||||||
|
amountConversionRate: 2,
|
||||||
|
balance: 1,
|
||||||
|
conversionRate: 3,
|
||||||
|
gasTotal: 17,
|
||||||
|
primaryCurrency: 'ABC',
|
||||||
|
expectedResult: { amount: INSUFFICIENT_FUNDS_ERROR },
|
||||||
|
},
|
||||||
|
'should return insufficientTokens error if token is selected and isTokenBalanceSufficient returns false': {
|
||||||
|
amount: '0x10',
|
||||||
|
amountConversionRate: 2,
|
||||||
|
balance: 100,
|
||||||
|
conversionRate: 3,
|
||||||
|
decimals: 10,
|
||||||
|
gasTotal: 17,
|
||||||
|
primaryCurrency: 'ABC',
|
||||||
|
selectedToken: 'someToken',
|
||||||
|
tokenBalance: 123,
|
||||||
|
expectedResult: { amount: INSUFFICIENT_TOKENS_ERROR },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Object.entries(config).map(([description, obj]) => {
|
||||||
|
it(description, () => {
|
||||||
|
assert.deepEqual(getAmountErrorObject(obj), obj.expectedResult)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getParamsForGasEstimate()', () => {
|
||||||
|
it('should return from and gas properties if no symbol or data', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getParamsForGasEstimate('mockAddress'),
|
||||||
|
{
|
||||||
|
from: 'mockAddress',
|
||||||
|
gas: '746a528800',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return value property if symbol provided', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getParamsForGasEstimate('mockAddress', 'ABC'),
|
||||||
|
{
|
||||||
|
from: 'mockAddress',
|
||||||
|
gas: '746a528800',
|
||||||
|
value: '0x0',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return data property if data provided', () => {
|
||||||
|
assert.deepEqual(
|
||||||
|
getParamsForGasEstimate('mockAddress', 'ABC', 'somedata'),
|
||||||
|
{
|
||||||
|
from: 'mockAddress',
|
||||||
|
gas: '746a528800',
|
||||||
|
value: '0x0',
|
||||||
|
data: 'somedata',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calcTokenBalance()', () => {
|
||||||
|
it('should return the calculated token blance', () => {
|
||||||
|
assert.equal(calcTokenBalance({
|
||||||
|
selectedToken: {
|
||||||
|
decimals: 11,
|
||||||
|
},
|
||||||
|
usersToken: {
|
||||||
|
balance: 20,
|
||||||
|
},
|
||||||
|
}), 'calc:2011')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isBalanceSufficient()', () => {
|
||||||
|
it('should correctly call addCurrencies and return the result of calling conversionGTE', () => {
|
||||||
|
stubs.conversionGTE.resetHistory()
|
||||||
|
const result = isBalanceSufficient({
|
||||||
|
amount: 15,
|
||||||
|
amountConversionRate: 2,
|
||||||
|
balance: 100,
|
||||||
|
conversionRate: 3,
|
||||||
|
gasTotal: 17,
|
||||||
|
primaryCurrency: 'ABC',
|
||||||
|
})
|
||||||
|
assert.deepEqual(
|
||||||
|
stubs.addCurrencies.getCall(0).args,
|
||||||
|
[
|
||||||
|
15, 17, {
|
||||||
|
aBase: 16,
|
||||||
|
bBase: 16,
|
||||||
|
toNumericBase: 'hex',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
assert.deepEqual(
|
||||||
|
stubs.conversionGTE.getCall(0).args,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: 100,
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
fromCurrency: 'ABC',
|
||||||
|
conversionRate: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 32,
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
conversionRate: 2,
|
||||||
|
fromCurrency: 'ABC',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(result, true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isTokenBalanceSufficient()', () => {
|
||||||
|
it('should correctly call conversionUtil and return the result of calling conversionGTE', () => {
|
||||||
|
stubs.conversionGTE.resetHistory()
|
||||||
|
const result = isTokenBalanceSufficient({
|
||||||
|
amount: '0x10',
|
||||||
|
tokenBalance: 123,
|
||||||
|
decimals: 10,
|
||||||
|
})
|
||||||
|
assert.deepEqual(
|
||||||
|
stubs.conversionUtil.getCall(0).args,
|
||||||
|
[
|
||||||
|
'0x10', {
|
||||||
|
fromNumericBase: 'hex',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
assert.deepEqual(
|
||||||
|
stubs.conversionGTE.getCall(0).args,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: 123,
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'calc:1610',
|
||||||
|
fromNumericBase: 'dec',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.equal(result, false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -203,8 +203,8 @@ TxListItem.prototype.showRetryButton = function () {
|
|||||||
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
|
const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
|
||||||
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
|
||||||
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
|
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
|
||||||
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
|
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
|
||||||
&& lastSubmittedTxWithCurrentNonce.id === transactionId
|
lastSubmittedTxWithCurrentNonce.id === transactionId
|
||||||
|
|
||||||
return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
|
return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ MainContainer.prototype.render = function () {
|
|||||||
// - pass resulting h() to MainContainer
|
// - pass resulting h() to MainContainer
|
||||||
// - error checking in separate func
|
// - error checking in separate func
|
||||||
// - router in separate func
|
// - router in separate func
|
||||||
let contents = {
|
const contents = {
|
||||||
component: AccountAndTransactionDetails,
|
component: AccountAndTransactionDetails,
|
||||||
key: 'account-detail',
|
key: 'account-detail',
|
||||||
style: {},
|
style: {},
|
||||||
|
Loading…
Reference in New Issue
Block a user