aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components/send_/send-content/send-amount-row
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components/send_/send-content/send-amount-row')
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/README.md0
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js54
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js40
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js9
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js22
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/index.js1
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js90
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js91
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js22
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js27
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/index.js1
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js96
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js51
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss0
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js9
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js164
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js109
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js34
18 files changed, 820 insertions, 0 deletions
diff --git a/ui/app/components/send_/send-content/send-amount-row/README.md b/ui/app/components/send_/send-content/send-amount-row/README.md
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/README.md
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js
new file mode 100644
index 000000000..bdf12b738
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.component.js
@@ -0,0 +1,54 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+
+export default class AmountMaxButton extends Component {
+
+ static propTypes = {
+ balance: PropTypes.string,
+ gasTotal: PropTypes.string,
+ maxModeOn: PropTypes.bool,
+ selectedToken: PropTypes.object,
+ setAmountToMax: PropTypes.func,
+ setMaxModeTo: PropTypes.func,
+ tokenBalance: PropTypes.string,
+ };
+
+ setMaxAmount () {
+ const {
+ balance,
+ gasTotal,
+ selectedToken,
+ setAmountToMax,
+ tokenBalance,
+ } = this.props
+
+ setAmountToMax({
+ balance,
+ gasTotal,
+ selectedToken,
+ tokenBalance,
+ })
+ }
+
+ render () {
+ const { setMaxModeTo, maxModeOn } = this.props
+
+ return (
+ <div
+ className="send-v2__amount-max"
+ onClick={(event) => {
+ event.preventDefault()
+ setMaxModeTo(true)
+ this.setMaxAmount()
+ }}
+ >
+ {!maxModeOn ? this.context.t('max') : ''}
+ </div>
+ )
+ }
+
+}
+
+AmountMaxButton.contextTypes = {
+ t: PropTypes.func,
+}
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js
new file mode 100644
index 000000000..2d2ec42f7
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.container.js
@@ -0,0 +1,40 @@
+import { connect } from 'react-redux'
+import {
+ getGasTotal,
+ getSelectedToken,
+ getSendFromBalance,
+ getTokenBalance,
+} from '../../../send.selectors.js'
+import { getMaxModeOn } from './amount-max-button.selectors.js'
+import { calcMaxAmount } from './amount-max-button.utils.js'
+import {
+ updateSendAmount,
+ setMaxModeTo,
+} from '../../../../../actions'
+import AmountMaxButton from './amount-max-button.component'
+import {
+ updateSendErrors,
+} from '../../../../../ducks/send.duck'
+
+export default connect(mapStateToProps, mapDispatchToProps)(AmountMaxButton)
+
+function mapStateToProps (state) {
+
+ return {
+ balance: getSendFromBalance(state),
+ gasTotal: getGasTotal(state),
+ maxModeOn: getMaxModeOn(state),
+ selectedToken: getSelectedToken(state),
+ tokenBalance: getTokenBalance(state),
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ setAmountToMax: maxAmountDataObject => {
+ dispatch(updateSendErrors({ amount: null }))
+ dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject)))
+ },
+ setMaxModeTo: bool => dispatch(setMaxModeTo(bool)),
+ }
+}
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js
new file mode 100644
index 000000000..69fec1994
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.selectors.js
@@ -0,0 +1,9 @@
+const selectors = {
+ getMaxModeOn,
+}
+
+module.exports = selectors
+
+function getMaxModeOn (state) {
+ return state.metamask.send.maxModeOn
+}
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js
new file mode 100644
index 000000000..b490a7fd7
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/amount-max-button.utils.js
@@ -0,0 +1,22 @@
+const {
+ multiplyCurrencies,
+ subtractCurrencies,
+} = require('../../../../../conversion-util')
+const ethUtil = require('ethereumjs-util')
+
+function calcMaxAmount ({ balance, gasTotal, selectedToken, tokenBalance }) {
+ const { decimals } = selectedToken || {}
+ const multiplier = Math.pow(10, Number(decimals || 0))
+
+ return selectedToken
+ ? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'})
+ : subtractCurrencies(
+ ethUtil.addHexPrefix(balance),
+ ethUtil.addHexPrefix(gasTotal),
+ { toNumericBase: 'hex' }
+ )
+}
+
+module.exports = {
+ calcMaxAmount,
+}
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/index.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/index.js
new file mode 100644
index 000000000..548b51f33
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/index.js
@@ -0,0 +1 @@
+export { default } from './amount-max-button.container' \ No newline at end of file
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js
new file mode 100644
index 000000000..86a05ff21
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-component.test.js
@@ -0,0 +1,90 @@
+import React from 'react'
+import assert from 'assert'
+import { shallow } from 'enzyme'
+import sinon from 'sinon'
+import AmountMaxButton from '../amount-max-button.component.js'
+
+const propsMethodSpies = {
+ setAmountToMax: sinon.spy(),
+ setMaxModeTo: sinon.spy(),
+}
+
+const MOCK_EVENT = { preventDefault: () => {} }
+
+sinon.spy(AmountMaxButton.prototype, 'setMaxAmount')
+
+describe('AmountMaxButton Component', function () {
+ let wrapper
+ let instance
+
+ beforeEach(() => {
+ wrapper = shallow(<AmountMaxButton
+ balance={'mockBalance'}
+ gasTotal={'mockGasTotal'}
+ maxModeOn={false}
+ selectedToken={ { address: 'mockTokenAddress' } }
+ setAmountToMax={propsMethodSpies.setAmountToMax}
+ setMaxModeTo={propsMethodSpies.setMaxModeTo}
+ tokenBalance={'mockTokenBalance'}
+ />, { context: { t: str => str + '_t' } })
+ instance = wrapper.instance()
+ })
+
+ afterEach(() => {
+ propsMethodSpies.setAmountToMax.resetHistory()
+ propsMethodSpies.setMaxModeTo.resetHistory()
+ AmountMaxButton.prototype.setMaxAmount.resetHistory()
+ })
+
+ describe('setMaxAmount', () => {
+
+ it('should call setAmountToMax with the correct params', () => {
+ assert.equal(propsMethodSpies.setAmountToMax.callCount, 0)
+ instance.setMaxAmount()
+ assert.equal(propsMethodSpies.setAmountToMax.callCount, 1)
+ assert.deepEqual(
+ propsMethodSpies.setAmountToMax.getCall(0).args,
+ [{
+ balance: 'mockBalance',
+ gasTotal: 'mockGasTotal',
+ selectedToken: { address: 'mockTokenAddress' },
+ tokenBalance: 'mockTokenBalance',
+ }]
+ )
+ })
+
+ })
+
+ describe('render', () => {
+ it('should render a div with a send-v2__amount-max class', () => {
+ assert.equal(wrapper.find('.send-v2__amount-max').length, 1)
+ assert(wrapper.find('.send-v2__amount-max').is('div'))
+ })
+
+ it('should call setMaxModeTo and setMaxAmount when the send-v2__amount-max div is clicked', () => {
+ const {
+ onClick,
+ } = wrapper.find('.send-v2__amount-max').props()
+
+ assert.equal(AmountMaxButton.prototype.setMaxAmount.callCount, 0)
+ assert.equal(propsMethodSpies.setMaxModeTo.callCount, 0)
+ onClick(MOCK_EVENT)
+ assert.equal(AmountMaxButton.prototype.setMaxAmount.callCount, 1)
+ assert.equal(propsMethodSpies.setMaxModeTo.callCount, 1)
+ assert.deepEqual(
+ propsMethodSpies.setMaxModeTo.getCall(0).args,
+ [true]
+ )
+ })
+
+ it('should not render text when maxModeOn is true', () => {
+ wrapper.setProps({ maxModeOn: true })
+ assert.equal(wrapper.find('.send-v2__amount-max').text(), '')
+ })
+
+ it('should render the expected text when maxModeOn is false', () => {
+ wrapper.setProps({ maxModeOn: false })
+ assert.equal(wrapper.find('.send-v2__amount-max').text(), 'max_t')
+ })
+ })
+})
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js
new file mode 100644
index 000000000..2cc00d6d6
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-container.test.js
@@ -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.duck': 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'
+ )
+ })
+ })
+
+ })
+
+})
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js
new file mode 100644
index 000000000..655fe1969
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-selectors.test.js
@@ -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)
+ })
+ })
+
+})
diff --git a/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js
new file mode 100644
index 000000000..816df6a12
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/amount-max-button/tests/amount-max-button-utils.test.js
@@ -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')
+ })
+ })
+
+})
diff --git a/ui/app/components/send_/send-content/send-amount-row/index.js b/ui/app/components/send_/send-content/send-amount-row/index.js
new file mode 100644
index 000000000..94a7da56f
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/index.js
@@ -0,0 +1 @@
+export { default } from './send-amount-row.container' \ No newline at end of file
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
new file mode 100644
index 000000000..8aefeed4a
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
@@ -0,0 +1,96 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import SendRowWrapper from '../send-row-wrapper/'
+import AmountMaxButton from './amount-max-button/'
+import CurrencyDisplay from '../../../send/currency-display'
+
+export default class SendAmountRow extends Component {
+
+ static propTypes = {
+ amount: PropTypes.string,
+ amountConversionRate: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ ]),
+ balance: PropTypes.string,
+ conversionRate: PropTypes.number,
+ convertedCurrency: PropTypes.string,
+ gasTotal: PropTypes.string,
+ inError: PropTypes.bool,
+ primaryCurrency: PropTypes.string,
+ selectedToken: PropTypes.object,
+ setMaxModeTo: PropTypes.func,
+ tokenBalance: PropTypes.string,
+ updateSendAmount: PropTypes.func,
+ updateSendAmountError: PropTypes.func,
+ }
+
+ validateAmount (amount) {
+ const {
+ amountConversionRate,
+ balance,
+ conversionRate,
+ gasTotal,
+ primaryCurrency,
+ selectedToken,
+ tokenBalance,
+ updateSendAmountError,
+ } = this.props
+
+ updateSendAmountError({
+ amount,
+ amountConversionRate,
+ balance,
+ conversionRate,
+ gasTotal,
+ primaryCurrency,
+ selectedToken,
+ tokenBalance,
+ })
+ }
+
+ updateAmount (amount) {
+ const { updateSendAmount, setMaxModeTo } = this.props
+
+ setMaxModeTo(false)
+ updateSendAmount(amount)
+ }
+
+ render () {
+ const {
+ amount,
+ amountConversionRate,
+ convertedCurrency,
+ gasTotal,
+ inError,
+ primaryCurrency,
+ selectedToken,
+ } = this.props
+
+ return (
+ <SendRowWrapper
+ label={`${this.context.t('amount')}:`}
+ showError={inError}
+ errorType={'amount'}
+ >
+ {!inError && gasTotal && <AmountMaxButton />}
+ <CurrencyDisplay
+ conversionRate={amountConversionRate}
+ convertedCurrency={convertedCurrency}
+ onBlur={newAmount => this.updateAmount(newAmount)}
+ onChange={newAmount => this.validateAmount(newAmount)}
+ inError={inError}
+ primaryCurrency={primaryCurrency || 'ETH'}
+ selectedToken={selectedToken}
+ value={amount || '0x0'}
+ />
+ </SendRowWrapper>
+ )
+ }
+
+}
+
+SendAmountRow.contextTypes = {
+ t: PropTypes.func,
+}
+
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
new file mode 100644
index 000000000..bbbf56971
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
@@ -0,0 +1,51 @@
+import { connect } from 'react-redux'
+import {
+ getAmountConversionRate,
+ getConversionRate,
+ getConvertedCurrency,
+ getGasTotal,
+ getPrimaryCurrency,
+ getSelectedToken,
+ getSendAmount,
+ getSendFromBalance,
+ getTokenBalance,
+} from '../../send.selectors'
+import {
+ sendAmountIsInError,
+} from './send-amount-row.selectors'
+import { getAmountErrorObject } from '../../send.utils'
+import {
+ setMaxModeTo,
+ updateSendAmount,
+} from '../../../../actions'
+import {
+ updateSendErrors,
+} from '../../../../ducks/send.duck'
+import SendAmountRow from './send-amount-row.component'
+
+export default connect(mapStateToProps, mapDispatchToProps)(SendAmountRow)
+
+function mapStateToProps (state) {
+ return {
+ amount: getSendAmount(state),
+ amountConversionRate: getAmountConversionRate(state),
+ balance: getSendFromBalance(state),
+ conversionRate: getConversionRate(state),
+ convertedCurrency: getConvertedCurrency(state),
+ gasTotal: getGasTotal(state),
+ inError: sendAmountIsInError(state),
+ primaryCurrency: getPrimaryCurrency(state),
+ selectedToken: getSelectedToken(state),
+ tokenBalance: getTokenBalance(state),
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ setMaxModeTo: bool => dispatch(setMaxModeTo(bool)),
+ updateSendAmount: newAmount => dispatch(updateSendAmount(newAmount)),
+ updateSendAmountError: (amountDataObject) => {
+ dispatch(updateSendErrors(getAmountErrorObject(amountDataObject)))
+ },
+ }
+}
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.scss
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js
new file mode 100644
index 000000000..fb08c7ed7
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.selectors.js
@@ -0,0 +1,9 @@
+const selectors = {
+ sendAmountIsInError,
+}
+
+module.exports = selectors
+
+function sendAmountIsInError (state) {
+ return Boolean(state.send.errors.amount)
+}
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
new file mode 100644
index 000000000..2205579ca
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
@@ -0,0 +1,164 @@
+import React from 'react'
+import assert from 'assert'
+import { shallow } from 'enzyme'
+import sinon from 'sinon'
+import SendAmountRow from '../send-amount-row.component.js'
+
+import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component'
+import AmountMaxButton from '../amount-max-button/amount-max-button.container'
+import CurrencyDisplay from '../../../../send/currency-display'
+
+const propsMethodSpies = {
+ setMaxModeTo: sinon.spy(),
+ updateSendAmount: sinon.spy(),
+ updateSendAmountError: sinon.spy(),
+}
+
+sinon.spy(SendAmountRow.prototype, 'updateAmount')
+sinon.spy(SendAmountRow.prototype, 'validateAmount')
+
+describe('SendAmountRow Component', function () {
+ let wrapper
+ let instance
+
+ beforeEach(() => {
+ wrapper = shallow(<SendAmountRow
+ amount={'mockAmount'}
+ amountConversionRate={'mockAmountConversionRate'}
+ balance={'mockBalance'}
+ conversionRate={7}
+ convertedCurrency={'mockConvertedCurrency'}
+ gasTotal={'mockGasTotal'}
+ inError={false}
+ primaryCurrency={'mockPrimaryCurrency'}
+ selectedToken={ { address: 'mockTokenAddress' } }
+ setMaxModeTo={propsMethodSpies.setMaxModeTo}
+ tokenBalance={'mockTokenBalance'}
+ updateSendAmount={propsMethodSpies.updateSendAmount}
+ updateSendAmountError={propsMethodSpies.updateSendAmountError}
+ />, { context: { t: str => str + '_t' } })
+ instance = wrapper.instance()
+ })
+
+ afterEach(() => {
+ propsMethodSpies.setMaxModeTo.resetHistory()
+ propsMethodSpies.updateSendAmount.resetHistory()
+ propsMethodSpies.updateSendAmountError.resetHistory()
+ SendAmountRow.prototype.validateAmount.resetHistory()
+ SendAmountRow.prototype.updateAmount.resetHistory()
+ })
+
+ describe('validateAmount', () => {
+
+ it('should call updateSendAmountError with the correct params', () => {
+ assert.equal(propsMethodSpies.updateSendAmountError.callCount, 0)
+ instance.validateAmount('someAmount')
+ assert.equal(propsMethodSpies.updateSendAmountError.callCount, 1)
+ assert.deepEqual(
+ propsMethodSpies.updateSendAmountError.getCall(0).args,
+ [{
+ amount: 'someAmount',
+ amountConversionRate: 'mockAmountConversionRate',
+ balance: 'mockBalance',
+ conversionRate: 7,
+ gasTotal: 'mockGasTotal',
+ primaryCurrency: 'mockPrimaryCurrency',
+ selectedToken: { address: 'mockTokenAddress' },
+ tokenBalance: 'mockTokenBalance',
+ }]
+ )
+ })
+
+ })
+
+ describe('updateAmount', () => {
+
+ it('should call setMaxModeTo', () => {
+ assert.equal(propsMethodSpies.setMaxModeTo.callCount, 0)
+ instance.updateAmount('someAmount')
+ assert.equal(propsMethodSpies.setMaxModeTo.callCount, 1)
+ assert.deepEqual(
+ propsMethodSpies.setMaxModeTo.getCall(0).args,
+ [false]
+ )
+ })
+
+ it('should call updateSendAmount', () => {
+ assert.equal(propsMethodSpies.updateSendAmount.callCount, 0)
+ instance.updateAmount('someAmount')
+ assert.equal(propsMethodSpies.updateSendAmount.callCount, 1)
+ assert.deepEqual(
+ propsMethodSpies.updateSendAmount.getCall(0).args,
+ ['someAmount']
+ )
+ })
+
+ })
+
+ describe('render', () => {
+ it('should render a SendRowWrapper component', () => {
+ assert.equal(wrapper.find(SendRowWrapper).length, 1)
+ })
+
+ it('should pass the correct props to SendRowWrapper', () => {
+ const {
+ errorType,
+ label,
+ showError,
+ } = wrapper.find(SendRowWrapper).props()
+
+ assert.equal(errorType, 'amount')
+
+ assert.equal(label, 'amount_t:')
+
+ assert.equal(showError, false)
+ })
+
+ it('should render an AmountMaxButton as the first child of the SendRowWrapper', () => {
+ assert(wrapper.find(SendRowWrapper).childAt(0).is(AmountMaxButton))
+ })
+
+ it('should render a CurrencyDisplay as the second child of the SendRowWrapper', () => {
+ assert(wrapper.find(SendRowWrapper).childAt(1).is(CurrencyDisplay))
+ })
+
+ it('should render the CurrencyDisplay with the correct props', () => {
+ const {
+ conversionRate,
+ convertedCurrency,
+ onBlur,
+ onChange,
+ inError,
+ primaryCurrency,
+ selectedToken,
+ value,
+ } = wrapper.find(SendRowWrapper).childAt(1).props()
+ assert.equal(conversionRate, 'mockAmountConversionRate')
+ assert.equal(convertedCurrency, 'mockConvertedCurrency')
+ assert.equal(inError, false)
+ assert.equal(primaryCurrency, 'mockPrimaryCurrency')
+ assert.deepEqual(selectedToken, { address: 'mockTokenAddress' })
+ assert.equal(value, 'mockAmount')
+ assert.equal(SendAmountRow.prototype.updateAmount.callCount, 0)
+ onBlur('mockNewAmount')
+ assert.equal(SendAmountRow.prototype.updateAmount.callCount, 1)
+ assert.deepEqual(
+ SendAmountRow.prototype.updateAmount.getCall(0).args,
+ ['mockNewAmount']
+ )
+ assert.equal(SendAmountRow.prototype.validateAmount.callCount, 0)
+ onChange('mockNewAmount')
+ assert.equal(SendAmountRow.prototype.validateAmount.callCount, 1)
+ assert.deepEqual(
+ SendAmountRow.prototype.validateAmount.getCall(0).args,
+ ['mockNewAmount']
+ )
+ })
+
+ it('should pass the default primaryCurrency to the CurrencyDisplay if primaryCurrency is falsy', () => {
+ wrapper.setProps({ primaryCurrency: null })
+ const { primaryCurrency } = wrapper.find(SendRowWrapper).childAt(1).props()
+ assert.equal(primaryCurrency, 'ETH')
+ })
+ })
+})
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
new file mode 100644
index 000000000..e4c913c69
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
@@ -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.duck': 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 }
+ )
+ })
+ })
+
+ })
+
+})
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js
new file mode 100644
index 000000000..4672cb8a7
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-selectors.test.js
@@ -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)
+ })
+ })
+
+})