From 284dd85a99f538b77fd477f4952117d1792f64a5 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 6 Apr 2018 19:59:51 -0230 Subject: first commit --- ui/app/components/send_/send-footer/README.md | 0 ui/app/components/send_/send-footer/send-footer.component.js | 0 ui/app/components/send_/send-footer/send-footer.container.js | 0 ui/app/components/send_/send-footer/send-footer.scss | 0 ui/app/components/send_/send-footer/send-footer.selectors.js | 0 ui/app/components/send_/send-footer/send-footer.utils.js | 0 ui/app/components/send_/send-footer/tests/send-footer-component.test.js | 0 ui/app/components/send_/send-footer/tests/send-footer-container.test.js | 0 ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js | 0 ui/app/components/send_/send-footer/tests/send-footer-utils.test.js | 0 10 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 ui/app/components/send_/send-footer/README.md create mode 100644 ui/app/components/send_/send-footer/send-footer.component.js create mode 100644 ui/app/components/send_/send-footer/send-footer.container.js create mode 100644 ui/app/components/send_/send-footer/send-footer.scss create mode 100644 ui/app/components/send_/send-footer/send-footer.selectors.js create mode 100644 ui/app/components/send_/send-footer/send-footer.utils.js create mode 100644 ui/app/components/send_/send-footer/tests/send-footer-component.test.js create mode 100644 ui/app/components/send_/send-footer/tests/send-footer-container.test.js create mode 100644 ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js create mode 100644 ui/app/components/send_/send-footer/tests/send-footer-utils.test.js (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/README.md b/ui/app/components/send_/send-footer/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/send-footer.component.js b/ui/app/components/send_/send-footer/send-footer.component.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send_/send-footer/send-footer.container.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/send-footer.scss b/ui/app/components/send_/send-footer/send-footer.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/send-footer.selectors.js b/ui/app/components/send_/send-footer/send-footer.selectors.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/send-footer.utils.js b/ui/app/components/send_/send-footer/send-footer.utils.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js b/ui/app/components/send_/send-footer/tests/send-footer-container.test.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js b/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js new file mode 100644 index 000000000..e69de29bb diff --git a/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js b/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js new file mode 100644 index 000000000..e69de29bb -- cgit From 8ff7806f1b471a90fa3f45ebc10f0f4452ade541 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 26 Apr 2018 14:08:38 -0230 Subject: Core of the refactor complete --- .../send_/send-footer/send-footer.component.js | 93 ++++++++++++++++++ .../send_/send-footer/send-footer.container.js | 107 +++++++++++++++++++++ .../send_/send-footer/send-footer.selectors.js | 12 +++ .../send_/send-footer/send-footer.utils.js | 84 ++++++++++++++++ 4 files changed, 296 insertions(+) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.component.js b/ui/app/components/send_/send-footer/send-footer.component.js index e69de29bb..64dd027cf 100644 --- a/ui/app/components/send_/send-footer/send-footer.component.js +++ b/ui/app/components/send_/send-footer/send-footer.component.js @@ -0,0 +1,93 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import PageContainerFooter from '../../page-container/page-container-footer.component' +import { CONFIRM_TRANSACTION_ROUTE, DEFAULT_ROUTE } from '../../../routes' + +export default class SendFooter extends Component { + + static propTypes = { + addToAddressBook: PropTypes.func, + amount: PropTypes.string, + clearSend: PropTypes.func, + editingTransactionId: PropTypes.string, + errors: PropTypes.object, + from: PropTypes.object, + gasLimit: PropTypes.string, + gasPrice: PropTypes.string, + gasTotal: PropTypes.string, + history: PropTypes.object, + selectedToken: PropTypes.object, + signTokenTx: PropTypes.func, + signTx: PropTypes.func, + to: PropTypes.string, + toAccounts: PropTypes.array, + tokenBalance: PropTypes.string, + unapprovedTxs: PropTypes.object, + updateTx: PropTypes.func, + }; + + onSubmit (event) { + event.preventDefault() + const { + addToAddressBookIfNew, + amount, + editingTransactionId, + from: {address: from}, + gasLimit: gas, + gasPrice, + selectedToken, + sign, + to, + unapprovedTxs, + // updateTx, + update, + toAccounts, + } = this.props + + // Should not be needed because submit should be disabled if there are no errors. + // const noErrors = !amountError && toError === null + + // if (!noErrors) { + // return + // } + + // TODO: add nickname functionality + addToAddressBookIfNew(to, toAccounts) + + editingTransactionId + ? update({ + from, + to, + amount, + gas, + gasPrice, + selectedToken, + editingTransactionId, + unapprovedTxs, + }) + : sign({ selectedToken, to, amount, from, gas, gasPrice }) + + this.props.history.push(CONFIRM_TRANSACTION_ROUTE) + } + + + render () { + const { clearSend, disabled, history } = this.props + + return ( + { + clearSend() + history.push(DEFAULT_ROUTE) + }} + onSubmit={e => this.onSubmit(e)} + disabled={disabled} + /> + ); + } + +} + +SendFooter.contextTypes = { + t: PropTypes.func, +} diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send_/send-footer/send-footer.container.js index e69de29bb..fff6e284f 100644 --- a/ui/app/components/send_/send-footer/send-footer.container.js +++ b/ui/app/components/send_/send-footer/send-footer.container.js @@ -0,0 +1,107 @@ +import { connect } from 'react-redux' +import ethUtil from 'ethereumjs-util' +import { + addToAddressBook, + clearSend, + goHome, + signTokenTx, + signTx, + updateTransaction, +} from '../../../actions' +import SendFooter from './send-footer.component' +import { + getGasLimit, + getGasPrice, + getGasTotal, + getSelectedToken, + getSendAmount, + getSendEditingTransactionId, + getSendFromObject, + getSendTo, + getSendToAccounts, + getTokenBalance, + getUnapprovedTxs, +} from '../send.selectors' +import { + isSendFormInError, +} from './send-footer.selectors' +import { + addressIsNew, + formShouldBeDisabled, + constructTxParams, +} from './send-footer.utils' + +export default connect(mapStateToProps, mapDispatchToProps)(SendFooter) + +function mapStateToProps (state) { + return { + isToken: Boolean(getSelectedToken(state)), + inError: isSendFormInError(state), + disabled: formShouldBeDisabled({ + inError: isSendFormInError(state), + selectedToken: getSelectedToken(state), + tokenBalance: getTokenBalance(state), + gasTotal: getGasTotal(state), + }), + amount: getSendAmount(state), + editingTransactionId: getSendEditingTransactionId(state), + from: getSendFromObject(state), + gasLimit: getGasLimit(state), + gasPrice: getGasPrice(state), + selectedToken: getSelectedToken(state), + to: getSendTo(state), + unapprovedTxs: getUnapprovedTxs(state), + toAccounts: getSendToAccounts(state), + } +} + +function mapDispatchToProps (dispatch) { + return { + goHome: () => dispatch(goHome()), + clearSend: () => dispatch(clearSend()), + sign: ({ selectedToken, to, amount, from, gas, gasPrice }) => { + const txParams = constructTxParams({ + amount, + from, + gas, + gasPrice, + selectedToken, + to, + }) + + selectedToken + ? dispatch(signTokenTx(selectedToken.address, to, amount, txParams)) + : dispatch(signTx(txParams)) + }, + update: ({ + amount, + editingTransactionId, + from, + gas, + gasPrice, + selectedToken, + to, + unapprovedTxs, + }) => { + const editingTx = constructUpdatedTx({ + amount, + editingTransactionId, + from, + gas, + gasPrice, + selectedToken, + to, + unapprovedTxs, + }) + + dispatch(updateTransaction(editingTx)) + }, + addToAddressBookIfNew: (newAddress, toAccounts, nickname = '') => { + const hexPrefixedAddress = ethUtil.addHexPrefix(newAddress) + if (addressIsNew(toAccounts)) { + // TODO: nickname, i.e. addToAddressBook(recipient, nickname) + dispatch(addToAddressBook(hexPrefixedAddress, nickname)) + } + } + } +} diff --git a/ui/app/components/send_/send-footer/send-footer.selectors.js b/ui/app/components/send_/send-footer/send-footer.selectors.js index e69de29bb..ccd4706ea 100644 --- a/ui/app/components/send_/send-footer/send-footer.selectors.js +++ b/ui/app/components/send_/send-footer/send-footer.selectors.js @@ -0,0 +1,12 @@ +import { getSendErrors } from '../send.selectors' + +const selectors = { + isSendFormInError, +} + +module.exports = selectors + +function isSendFormInError (state) { + const { amount, to } = getSendErrors(state) + return Boolean(amount || to !== null) +} \ No newline at end of file diff --git a/ui/app/components/send_/send-footer/send-footer.utils.js b/ui/app/components/send_/send-footer/send-footer.utils.js index e69de29bb..23d5655c7 100644 --- a/ui/app/components/send_/send-footer/send-footer.utils.js +++ b/ui/app/components/send_/send-footer/send-footer.utils.js @@ -0,0 +1,84 @@ +import ethAbi from 'ethereumjs-abi' +import ethUtil from 'ethereumjs-util' +import { TOKEN_TRANSFER_FUNCTION_SIGNATURE } from '../send.constants' + +function formShouldBeDisabled ({ inError, selectedToken, tokenBalance, gasTotal }) { + const missingTokenBalance = selectedToken && !tokenBalance + return inError || !gasTotal || missingTokenBalance +} + +function addHexPrefixToObjectValues (obj) { + return Object.keys(obj).reduce((newObj, key) => { + return { ...newObj, [key]: ethUtil.addHexPrefix(obj[key]) } + }, {}) +} + +function constructTxParams ({ selectedToken, to, amount, from, gas, gasPrice }) { + const txParams = { + from, + value: '0', + gas, + gasPrice, + } + + if (!selectedToken) { + txParams.value = amount + txParams.to = to + } + + const hexPrefixedTxParams = addHexPrefixToObjectValues(txParams) + + return hexPrefixedTxParams +} + +function constructUpdatedTx ({ + amount, + editingTransactionId, + from, + gas, + gasPrice, + selectedToken, + to, + unapprovedTxs, +}) { + const editingTx = { + ...unapprovedTxs[editingTransactionId], + txParams: addHexPrefixToObjectValues({ from, gas, gasPrice }), + } + + if (selectedToken) { + const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call( + ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]), + x => ('00' + x.toString(16)).slice(-2) + ).join('') + + Object.assign(editingTx.txParams, addHexPrefixToObjectValues({ + value: '0', + to: selectedToken.address, + data, + })) + } else { + const { data } = unapprovedTxs[editingTransactionId].txParams + + Object.assign(editingTx.txParams, addHexPrefixToObjectValues({ + value: amount, + to, + data, + })) + + if (typeof editingTx.txParams.data === 'undefined') { + delete editingTx.txParams.data + } + } +} + +function addressIsNew (toAccounts, newAddress) { + return !toAccounts.find(({ address }) => newAddress === address) +} + +module.exports = { + addressIsNew, + formShouldBeDisabled, + constructTxParams, + constructUpdatedTx, +} -- cgit From 91c201aa72581a59a0d2ef73a225b1768584dea7 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 26 Apr 2018 22:08:14 -0230 Subject: Lint fixes and alphabetization for i3725-refactor-send-component --- .../send_/send-footer/send-footer.component.js | 18 +++++++++--------- .../send_/send-footer/send-footer.container.js | 15 ++++++++------- .../send_/send-footer/send-footer.selectors.js | 2 +- .../components/send_/send-footer/send-footer.utils.js | 2 +- 4 files changed, 19 insertions(+), 18 deletions(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.component.js b/ui/app/components/send_/send-footer/send-footer.component.js index 64dd027cf..ffece433e 100644 --- a/ui/app/components/send_/send-footer/send-footer.component.js +++ b/ui/app/components/send_/send-footer/send-footer.component.js @@ -6,24 +6,24 @@ import { CONFIRM_TRANSACTION_ROUTE, DEFAULT_ROUTE } from '../../../routes' export default class SendFooter extends Component { static propTypes = { - addToAddressBook: PropTypes.func, + addToAddressBookIfNew: PropTypes.func, amount: PropTypes.string, clearSend: PropTypes.func, + disabled: PropTypes.bool, editingTransactionId: PropTypes.string, - errors: PropTypes.object, + errors: PropTypes.object, from: PropTypes.object, gasLimit: PropTypes.string, gasPrice: PropTypes.string, gasTotal: PropTypes.string, history: PropTypes.object, selectedToken: PropTypes.object, - signTokenTx: PropTypes.func, - signTx: PropTypes.func, + sign: PropTypes.func, to: PropTypes.string, toAccounts: PropTypes.array, tokenBalance: PropTypes.string, unapprovedTxs: PropTypes.object, - updateTx: PropTypes.func, + update: PropTypes.func, }; onSubmit (event) { @@ -56,13 +56,13 @@ export default class SendFooter extends Component { editingTransactionId ? update({ - from, - to, amount, + editingTransactionId, + from, gas, gasPrice, selectedToken, - editingTransactionId, + to, unapprovedTxs, }) : sign({ selectedToken, to, amount, from, gas, gasPrice }) @@ -83,7 +83,7 @@ export default class SendFooter extends Component { onSubmit={e => this.onSubmit(e)} disabled={disabled} /> - ); + ) } } diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send_/send-footer/send-footer.container.js index fff6e284f..288c4daf2 100644 --- a/ui/app/components/send_/send-footer/send-footer.container.js +++ b/ui/app/components/send_/send-footer/send-footer.container.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux' -import ethUtil from 'ethereumjs-util' +import ethUtil from 'ethereumjs-util' import { addToAddressBook, clearSend, @@ -27,31 +27,32 @@ import { } from './send-footer.selectors' import { addressIsNew, - formShouldBeDisabled, constructTxParams, + constructUpdatedTx, + formShouldBeDisabled, } from './send-footer.utils' export default connect(mapStateToProps, mapDispatchToProps)(SendFooter) function mapStateToProps (state) { return { - isToken: Boolean(getSelectedToken(state)), - inError: isSendFormInError(state), + amount: getSendAmount(state), disabled: formShouldBeDisabled({ inError: isSendFormInError(state), selectedToken: getSelectedToken(state), tokenBalance: getTokenBalance(state), gasTotal: getGasTotal(state), }), - amount: getSendAmount(state), editingTransactionId: getSendEditingTransactionId(state), from: getSendFromObject(state), gasLimit: getGasLimit(state), gasPrice: getGasPrice(state), + inError: isSendFormInError(state), + isToken: Boolean(getSelectedToken(state)), selectedToken: getSelectedToken(state), to: getSendTo(state), - unapprovedTxs: getUnapprovedTxs(state), toAccounts: getSendToAccounts(state), + unapprovedTxs: getUnapprovedTxs(state), } } @@ -102,6 +103,6 @@ function mapDispatchToProps (dispatch) { // TODO: nickname, i.e. addToAddressBook(recipient, nickname) dispatch(addToAddressBook(hexPrefixedAddress, nickname)) } - } + }, } } diff --git a/ui/app/components/send_/send-footer/send-footer.selectors.js b/ui/app/components/send_/send-footer/send-footer.selectors.js index ccd4706ea..e8fef6be6 100644 --- a/ui/app/components/send_/send-footer/send-footer.selectors.js +++ b/ui/app/components/send_/send-footer/send-footer.selectors.js @@ -9,4 +9,4 @@ module.exports = selectors function isSendFormInError (state) { const { amount, to } = getSendErrors(state) return Boolean(amount || to !== null) -} \ No newline at end of file +} diff --git a/ui/app/components/send_/send-footer/send-footer.utils.js b/ui/app/components/send_/send-footer/send-footer.utils.js index 23d5655c7..353c0e347 100644 --- a/ui/app/components/send_/send-footer/send-footer.utils.js +++ b/ui/app/components/send_/send-footer/send-footer.utils.js @@ -1,5 +1,5 @@ import ethAbi from 'ethereumjs-abi' -import ethUtil from 'ethereumjs-util' +import ethUtil from 'ethereumjs-util' import { TOKEN_TRANSFER_FUNCTION_SIGNATURE } from '../send.constants' function formShouldBeDisabled ({ inError, selectedToken, tokenBalance, gasTotal }) { -- cgit From 954394f81090b1a6a4afe55243caa3671b88addc Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 30 Apr 2018 14:08:03 -0230 Subject: Remove 'goHome' from send_ --- ui/app/components/send_/send-footer/send-footer.container.js | 2 -- 1 file changed, 2 deletions(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send_/send-footer/send-footer.container.js index 288c4daf2..022b2db08 100644 --- a/ui/app/components/send_/send-footer/send-footer.container.js +++ b/ui/app/components/send_/send-footer/send-footer.container.js @@ -3,7 +3,6 @@ import ethUtil from 'ethereumjs-util' import { addToAddressBook, clearSend, - goHome, signTokenTx, signTx, updateTransaction, @@ -58,7 +57,6 @@ function mapStateToProps (state) { function mapDispatchToProps (dispatch) { return { - goHome: () => dispatch(goHome()), clearSend: () => dispatch(clearSend()), sign: ({ selectedToken, to, amount, from, gas, gasPrice }) => { const txParams = constructTxParams({ -- cgit From 7c490098548522c16be1b1e84bce37f5bf87f1f4 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 5 May 2018 11:11:53 -0400 Subject: Unit tests for containers, utils and selectors in send_/ --- .../send_/send-footer/send-footer.container.js | 4 +- .../send_/send-footer/send-footer.selectors.js | 2 +- .../send_/send-footer/send-footer.utils.js | 10 +- .../tests/send-footer-container.test.js | 202 +++++++++++++++++ .../tests/send-footer-selectors.test.js | 0 .../send-footer/tests/send-footer-utils.test.js | 242 +++++++++++++++++++++ 6 files changed, 453 insertions(+), 7 deletions(-) delete mode 100644 ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send_/send-footer/send-footer.container.js index 022b2db08..0242dfa54 100644 --- a/ui/app/components/send_/send-footer/send-footer.container.js +++ b/ui/app/components/send_/send-footer/send-footer.container.js @@ -20,10 +20,8 @@ import { getSendToAccounts, getTokenBalance, getUnapprovedTxs, -} from '../send.selectors' -import { isSendFormInError, -} from './send-footer.selectors' +} from '../send.selectors' import { addressIsNew, constructTxParams, diff --git a/ui/app/components/send_/send-footer/send-footer.selectors.js b/ui/app/components/send_/send-footer/send-footer.selectors.js index e8fef6be6..15a053ae0 100644 --- a/ui/app/components/send_/send-footer/send-footer.selectors.js +++ b/ui/app/components/send_/send-footer/send-footer.selectors.js @@ -1,4 +1,4 @@ -import { getSendErrors } from '../send.selectors' +const { getSendErrors } = require('../send.selectors') const selectors = { isSendFormInError, diff --git a/ui/app/components/send_/send-footer/send-footer.utils.js b/ui/app/components/send_/send-footer/send-footer.utils.js index 353c0e347..149d9e357 100644 --- a/ui/app/components/send_/send-footer/send-footer.utils.js +++ b/ui/app/components/send_/send-footer/send-footer.utils.js @@ -1,6 +1,6 @@ -import ethAbi from 'ethereumjs-abi' -import ethUtil from 'ethereumjs-util' -import { TOKEN_TRANSFER_FUNCTION_SIGNATURE } from '../send.constants' +const ethAbi = require('ethereumjs-abi') +const ethUtil = require('ethereumjs-util') +const { TOKEN_TRANSFER_FUNCTION_SIGNATURE } = require('../send.constants') function formShouldBeDisabled ({ inError, selectedToken, tokenBalance, gasTotal }) { const missingTokenBalance = selectedToken && !tokenBalance @@ -47,6 +47,7 @@ function constructUpdatedTx ({ } if (selectedToken) { + console.log(`ethAbi.rawEncode`, ethAbi.rawEncode) const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call( ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]), x => ('00' + x.toString(16)).slice(-2) @@ -70,6 +71,8 @@ function constructUpdatedTx ({ delete editingTx.txParams.data } } + + return editingTx } function addressIsNew (toAccounts, newAddress) { @@ -81,4 +84,5 @@ module.exports = { formShouldBeDisabled, constructTxParams, constructUpdatedTx, + addHexPrefixToObjectValues, } diff --git a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js b/ui/app/components/send_/send-footer/tests/send-footer-container.test.js index e69de29bb..e9d4eb04d 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-container.test.js @@ -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' ] + ) + }) + }) + + }) + +}) diff --git a/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js b/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js b/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js index e69de29bb..b235ea5e5 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js @@ -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`, + }, + }) + }) + }) + +}) -- cgit From 9ccc609e567b373b5f02bddc4d1095b2ce2d6c44 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 7 May 2018 08:03:20 -0400 Subject: Adds test for send, send-content, send-footer and send-header components. --- .../send_/send-footer/send-footer.component.js | 17 ++- .../tests/send-footer-component.test.js | 159 +++++++++++++++++++++ 2 files changed, 167 insertions(+), 9 deletions(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.component.js b/ui/app/components/send_/send-footer/send-footer.component.js index fc7a78a94..de2a885f0 100644 --- a/ui/app/components/send_/send-footer/send-footer.component.js +++ b/ui/app/components/send_/send-footer/send-footer.component.js @@ -26,6 +26,11 @@ export default class SendFooter extends Component { update: PropTypes.func, }; + onCancel () { + this.props.clearSend() + this.props.history.push(DEFAULT_ROUTE) + } + onSubmit (event) { event.preventDefault() const { @@ -44,7 +49,7 @@ export default class SendFooter extends Component { toAccounts, } = this.props - // Should not be needed because submit should be disabled if there are no errors. + // Should not be needed because submit should be disabled if there are errors. // const noErrors = !amountError && toError === null // if (!noErrors) { @@ -70,18 +75,12 @@ export default class SendFooter extends Component { this.props.history.push(CONFIRM_TRANSACTION_ROUTE) } - render () { - const { clearSend, disabled, history } = this.props - return ( { - clearSend() - history.push(DEFAULT_ROUTE) - }} + onCancel={() => this.onCancel()} onSubmit={e => this.onSubmit(e)} - disabled={disabled} + disabled={this.props.disabled} /> ) } diff --git a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js index e69de29bb..f8cbd41f3 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js @@ -0,0 +1,159 @@ +import React from 'react' +import assert from 'assert' +import { shallow } from 'enzyme' +import sinon from 'sinon' +import { CONFIRM_TRANSACTION_ROUTE, DEFAULT_ROUTE } from '../../../../routes' +import SendFooter from '../send-footer.component.js' + +import PageContainerFooter from '../../../page-container/page-container-footer' + +const propsMethodSpies = { + addToAddressBookIfNew: sinon.spy(), + clearSend: sinon.spy(), + sign: sinon.spy(), + update: sinon.spy(), +} +const historySpies = { + push: sinon.spy(), +} +const MOCK_EVENT = { preventDefault: () => {} } + +sinon.spy(SendFooter.prototype, 'onCancel') +sinon.spy(SendFooter.prototype, 'onSubmit') + +describe('Send Component', function () { + let wrapper + + beforeEach(() => { + wrapper = shallow(, { context: { t: str => str } }) + instance = wrapper.instance() + }) + + afterEach(() => { + propsMethodSpies.clearSend.resetHistory() + propsMethodSpies.addToAddressBookIfNew.resetHistory() + propsMethodSpies.clearSend.resetHistory() + propsMethodSpies.sign.resetHistory() + propsMethodSpies.update.resetHistory() + historySpies.push.resetHistory() + SendFooter.prototype.onCancel.resetHistory() + SendFooter.prototype.onSubmit.resetHistory() + }) + + describe('onCancel', () => { + it('should call clearSend', () => { + assert.equal(propsMethodSpies.clearSend.callCount, 0) + wrapper.instance().onCancel() + assert.equal(propsMethodSpies.clearSend.callCount, 1) + }) + + it('should call history.push', () => { + assert.equal(historySpies.push.callCount, 0) + wrapper.instance().onCancel() + assert.equal(historySpies.push.callCount, 1) + assert.equal(historySpies.push.getCall(0).args[0], DEFAULT_ROUTE) + }) + }) + + describe('onSubmit', () => { + it('should call addToAddressBookIfNew with the correct params', () => { + wrapper.instance().onSubmit(MOCK_EVENT) + assert(propsMethodSpies.addToAddressBookIfNew.calledOnce) + assert.deepEqual( + propsMethodSpies.addToAddressBookIfNew.getCall(0).args, + ['mockTo', ['mockAccount']] + ) + }) + + it('should call props.update if editingTransactionId is truthy', () => { + wrapper.instance().onSubmit(MOCK_EVENT) + assert(propsMethodSpies.update.calledOnce) + assert.deepEqual( + propsMethodSpies.update.getCall(0).args[0], + { + amount: 'mockAmount', + editingTransactionId: 'mockEditingTransactionId', + from: 'mockAddress', + gas: 'mockGasLimit', + gasPrice: 'mockGasPrice', + selectedToken: { mockProp: 'mockSelectedTokenProp' }, + to: 'mockTo', + unapprovedTxs: ['mockTx'], + } + ) + }) + + it('should not call props.sign if editingTransactionId is truthy', () => { + assert.equal(propsMethodSpies.sign.callCount, 0) + }) + + it('should call props.sign if editingTransactionId is falsy', () => { + wrapper.setProps({ editingTransactionId: null }) + wrapper.instance().onSubmit(MOCK_EVENT) + assert(propsMethodSpies.sign.calledOnce) + assert.deepEqual( + propsMethodSpies.sign.getCall(0).args[0], + { + amount: 'mockAmount', + from: 'mockAddress', + gas: 'mockGasLimit', + gasPrice: 'mockGasPrice', + selectedToken: { mockProp: 'mockSelectedTokenProp' }, + to: 'mockTo', + } + ) + }) + + it('should not call props.update if editingTransactionId is falsy', () => { + assert.equal(propsMethodSpies.update.callCount, 0) + }) + + it('should call history.push', () => { + wrapper.instance().onSubmit(MOCK_EVENT) + assert.equal(historySpies.push.callCount, 1) + assert.equal(historySpies.push.getCall(0).args[0], CONFIRM_TRANSACTION_ROUTE) + }) + }) + + describe('render', () => { + it('should render a PageContainerFooter component', () => { + assert.equal(wrapper.find(PageContainerFooter).length, 1) + }) + + it('should pass the correct props to PageContainerFooter', () => { + const { + onCancel, + onSubmit, + disabled, + } = wrapper.find(PageContainerFooter).props() + assert.equal(disabled, true) + + assert.equal(SendFooter.prototype.onSubmit.callCount, 0) + onSubmit(MOCK_EVENT) + assert.equal(SendFooter.prototype.onSubmit.callCount, 1) + + assert.equal(SendFooter.prototype.onCancel.callCount, 0) + onCancel() + assert.equal(SendFooter.prototype.onCancel.callCount, 1) + }) + }) +}) -- cgit From f94ffa022c9f76a8d7acbf331f49a363d5e7362d Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 10 May 2018 07:37:33 -0400 Subject: Fix test descriptions and remove unnecessary proptypes. --- .../components/send_/send-footer/tests/send-footer-component.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js index f8cbd41f3..4689434d4 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js @@ -21,8 +21,9 @@ const MOCK_EVENT = { preventDefault: () => {} } sinon.spy(SendFooter.prototype, 'onCancel') sinon.spy(SendFooter.prototype, 'onSubmit') -describe('Send Component', function () { +describe('SendFooter Component', function () { let wrapper + let instance beforeEach(() => { wrapper = shallow( Date: Mon, 14 May 2018 07:01:41 -0230 Subject: Lint fixes --- ui/app/components/send_/send-footer/tests/send-footer-component.test.js | 2 -- 1 file changed, 2 deletions(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js index 4689434d4..c0b8f956f 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js @@ -23,7 +23,6 @@ sinon.spy(SendFooter.prototype, 'onSubmit') describe('SendFooter Component', function () { let wrapper - let instance beforeEach(() => { wrapper = shallow(, { context: { t: str => str } }) - instance = wrapper.instance() }) afterEach(() => { -- cgit From 77ee23d4935c72816e4d141472f53d8ade170cdf Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 14 May 2018 20:33:04 -0230 Subject: Add index.js files to send_ subdirectories --- ui/app/components/send_/send-footer/index.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 ui/app/components/send_/send-footer/index.js (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/index.js b/ui/app/components/send_/send-footer/index.js new file mode 100644 index 000000000..cd1727330 --- /dev/null +++ b/ui/app/components/send_/send-footer/index.js @@ -0,0 +1 @@ +export { default } from './send-footer.container' \ No newline at end of file -- cgit From 3a87d9221d7ae1d0d7cb526e5fb22ea528498105 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 24 May 2018 18:25:23 -0230 Subject: Fix order of button text in page-container-footer. --- ui/app/components/send_/send-footer/send-footer.container.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send_/send-footer/send-footer.container.js index 0242dfa54..64e4b4bd3 100644 --- a/ui/app/components/send_/send-footer/send-footer.container.js +++ b/ui/app/components/send_/send-footer/send-footer.container.js @@ -26,7 +26,6 @@ import { addressIsNew, constructTxParams, constructUpdatedTx, - formShouldBeDisabled, } from './send-footer.utils' export default connect(mapStateToProps, mapDispatchToProps)(SendFooter) @@ -34,21 +33,16 @@ export default connect(mapStateToProps, mapDispatchToProps)(SendFooter) function mapStateToProps (state) { return { amount: getSendAmount(state), - disabled: formShouldBeDisabled({ - inError: isSendFormInError(state), - selectedToken: getSelectedToken(state), - tokenBalance: getTokenBalance(state), - gasTotal: getGasTotal(state), - }), editingTransactionId: getSendEditingTransactionId(state), from: getSendFromObject(state), gasLimit: getGasLimit(state), gasPrice: getGasPrice(state), + gasTotal: getGasTotal(state), inError: isSendFormInError(state), - isToken: Boolean(getSelectedToken(state)), selectedToken: getSelectedToken(state), to: getSendTo(state), toAccounts: getSendToAccounts(state), + tokenBalance: getTokenBalance(state), unapprovedTxs: getUnapprovedTxs(state), } } -- cgit From dc2b5d0ef47be2125e58018470539d65d0d64c75 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 24 May 2018 22:23:54 -0230 Subject: Move formShouldBeDisabled from send-footer util to component. --- .../send_/send-footer/send-footer.component.js | 9 ++- .../send_/send-footer/send-footer.utils.js | 6 -- .../tests/send-footer-component.test.js | 65 +++++++++++++++++++++- .../send-footer/tests/send-footer-utils.test.js | 32 ----------- 4 files changed, 72 insertions(+), 40 deletions(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.component.js b/ui/app/components/send_/send-footer/send-footer.component.js index de2a885f0..7ff44c9d5 100644 --- a/ui/app/components/send_/send-footer/send-footer.component.js +++ b/ui/app/components/send_/send-footer/send-footer.component.js @@ -17,6 +17,7 @@ export default class SendFooter extends Component { gasPrice: PropTypes.string, gasTotal: PropTypes.string, history: PropTypes.object, + inError: PropTypes.bool, selectedToken: PropTypes.object, sign: PropTypes.func, to: PropTypes.string, @@ -75,12 +76,18 @@ export default class SendFooter extends Component { this.props.history.push(CONFIRM_TRANSACTION_ROUTE) } + formShouldBeDisabled () { + const { inError, selectedToken, tokenBalance, gasTotal } = this.props + const missingTokenBalance = selectedToken && !tokenBalance + return inError || !gasTotal || missingTokenBalance + } + render () { return ( this.onCancel()} onSubmit={e => this.onSubmit(e)} - disabled={this.props.disabled} + disabled={this.formShouldBeDisabled()} /> ) } diff --git a/ui/app/components/send_/send-footer/send-footer.utils.js b/ui/app/components/send_/send-footer/send-footer.utils.js index 149d9e357..d5639629d 100644 --- a/ui/app/components/send_/send-footer/send-footer.utils.js +++ b/ui/app/components/send_/send-footer/send-footer.utils.js @@ -2,11 +2,6 @@ const ethAbi = require('ethereumjs-abi') const ethUtil = require('ethereumjs-util') const { TOKEN_TRANSFER_FUNCTION_SIGNATURE } = require('../send.constants') -function formShouldBeDisabled ({ inError, selectedToken, tokenBalance, gasTotal }) { - const missingTokenBalance = selectedToken && !tokenBalance - return inError || !gasTotal || missingTokenBalance -} - function addHexPrefixToObjectValues (obj) { return Object.keys(obj).reduce((newObj, key) => { return { ...newObj, [key]: ethUtil.addHexPrefix(obj[key]) } @@ -81,7 +76,6 @@ function addressIsNew (toAccounts, newAddress) { module.exports = { addressIsNew, - formShouldBeDisabled, constructTxParams, constructUpdatedTx, addHexPrefixToObjectValues, diff --git a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js index c0b8f956f..a74b6195c 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js @@ -37,6 +37,7 @@ describe('SendFooter Component', function () { gasPrice={'mockGasPrice'} gasTotal={'mockGasTotal'} history={historySpies} + inError={false} selectedToken={{ mockProp: 'mockSelectedTokenProp' }} sign={propsMethodSpies.sign} to={'mockTo'} @@ -73,6 +74,39 @@ describe('SendFooter Component', function () { }) }) + + 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, () => { + wrapper.setProps(obj) + assert.equal(wrapper.instance().formShouldBeDisabled(), obj.expectedResult) + }) + }) + }) + describe('onSubmit', () => { it('should call addToAddressBookIfNew with the correct params', () => { wrapper.instance().onSubmit(MOCK_EVENT) @@ -134,6 +168,35 @@ describe('SendFooter Component', function () { }) describe('render', () => { + beforeEach(() => { + sinon.stub(SendFooter.prototype, 'formShouldBeDisabled').returns('formShouldBeDisabledReturn') + wrapper = shallow(, { context: { t: str => str } }) + }) + + afterEach(() => { + SendFooter.prototype.formShouldBeDisabled.restore() + }) + it('should render a PageContainerFooter component', () => { assert.equal(wrapper.find(PageContainerFooter).length, 1) }) @@ -144,7 +207,7 @@ describe('SendFooter Component', function () { onSubmit, disabled, } = wrapper.find(PageContainerFooter).props() - assert.equal(disabled, true) + assert.equal(disabled, 'formShouldBeDisabledReturn') assert.equal(SendFooter.prototype.onSubmit.callCount, 0) onSubmit(MOCK_EVENT) diff --git a/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js b/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js index b235ea5e5..2d3135995 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-utils.test.js @@ -16,7 +16,6 @@ const sendUtils = proxyquire('../send-footer.utils.js', { }) const { addressIsNew, - formShouldBeDisabled, constructTxParams, constructUpdatedTx, addHexPrefixToObjectValues, @@ -65,37 +64,6 @@ describe('send-footer utils', () => { }) }) - 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( -- cgit From 0de765aa25637cd85e22eebd11b6c4c8a32faf14 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 24 May 2018 22:30:07 -0230 Subject: Clean up for send refactor PR. --- .../send_/send-footer/tests/send-footer-container.test.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js b/ui/app/components/send_/send-footer/tests/send-footer-container.test.js index e9d4eb04d..9a616777e 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-container.test.js @@ -16,7 +16,6 @@ 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', { @@ -53,27 +52,18 @@ describe('send-footer container', () => { 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', + gasTotal: 'mockGasTotal:mockState', inError: 'mockInError:mockState', - isToken: true, to: 'mockTo:mockState', toAccounts: 'mockToAccounts:mockState', + tokenBalance: 'mockTokenBalance:mockState', unapprovedTxs: 'mockUnapprovedTxs:mockState', }) - assert.deepEqual( - utilsStubs.formShouldBeDisabled.getCall(0).args[0], - { - inError: 'mockInError:mockState', - selectedToken: 'mockSelectedToken:mockState', - tokenBalance: 'mockTokenBalance:mockState', - gasTotal: 'mockGasTotal:mockState', - } - ) }) }) -- cgit From e712336189e1a0a453ea30dbb58abbc3c57db8f8 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 25 May 2018 14:39:31 -0230 Subject: Send refactor: fix error handling and form disabling in send form. --- .../send_/send-footer/send-footer.component.js | 4 ++-- .../send_/send-footer/send-footer.container.js | 4 +++- .../send_/send-footer/send-footer.selectors.js | 3 +-- .../tests/send-footer-component.test.js | 6 ++++++ .../tests/send-footer-container.test.js | 3 +-- .../tests/send-footer-selectors.test.js | 24 ++++++++++++++++++++++ 6 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.component.js b/ui/app/components/send_/send-footer/send-footer.component.js index 7ff44c9d5..6471ae1a3 100644 --- a/ui/app/components/send_/send-footer/send-footer.component.js +++ b/ui/app/components/send_/send-footer/send-footer.component.js @@ -77,9 +77,9 @@ export default class SendFooter extends Component { } formShouldBeDisabled () { - const { inError, selectedToken, tokenBalance, gasTotal } = this.props + const { inError, selectedToken, tokenBalance, gasTotal, to } = this.props const missingTokenBalance = selectedToken && !tokenBalance - return inError || !gasTotal || missingTokenBalance + return inError || !gasTotal || missingTokenBalance || !to } render () { diff --git a/ui/app/components/send_/send-footer/send-footer.container.js b/ui/app/components/send_/send-footer/send-footer.container.js index 64e4b4bd3..260ff40bc 100644 --- a/ui/app/components/send_/send-footer/send-footer.container.js +++ b/ui/app/components/send_/send-footer/send-footer.container.js @@ -20,8 +20,10 @@ import { getSendToAccounts, getTokenBalance, getUnapprovedTxs, - isSendFormInError, } from '../send.selectors' +import { + isSendFormInError, +} from './send-footer.selectors' import { addressIsNew, constructTxParams, diff --git a/ui/app/components/send_/send-footer/send-footer.selectors.js b/ui/app/components/send_/send-footer/send-footer.selectors.js index 15a053ae0..e20addfdc 100644 --- a/ui/app/components/send_/send-footer/send-footer.selectors.js +++ b/ui/app/components/send_/send-footer/send-footer.selectors.js @@ -7,6 +7,5 @@ const selectors = { module.exports = selectors function isSendFormInError (state) { - const { amount, to } = getSendErrors(state) - return Boolean(amount || to !== null) + return Object.values(getSendErrors(state)).some(n => n) } diff --git a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js index a74b6195c..e071fe54f 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-component.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-component.test.js @@ -86,6 +86,12 @@ describe('SendFooter Component', function () { gasTotal: false, expectedResult: true, }, + 'should return true if to is truthy': { + to: '0xsomevalidAddress', + inError: false, + gasTotal: false, + expectedResult: true, + }, 'should return true if selectedToken is truthy and tokenBalance is falsy': { selectedToken: true, tokenBalance: null, diff --git a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js b/ui/app/components/send_/send-footer/tests/send-footer-container.test.js index 9a616777e..39d6a7686 100644 --- a/ui/app/components/send_/send-footer/tests/send-footer-container.test.js +++ b/ui/app/components/send_/send-footer/tests/send-footer-container.test.js @@ -39,9 +39,8 @@ proxyquire('../send-footer.container.js', { getSendToAccounts: (s) => `mockToAccounts:${s}`, getTokenBalance: (s) => `mockTokenBalance:${s}`, getUnapprovedTxs: (s) => `mockUnapprovedTxs:${s}`, - isSendFormInError: (s) => `mockInError:${s}`, }, - './send-footer.selectors': { isSendFormInError: () => {} }, + './send-footer.selectors': { isSendFormInError: (s) => `mockInError:${s}` }, './send-footer.utils': utilsStubs, }) diff --git a/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js b/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js new file mode 100644 index 000000000..8de032f57 --- /dev/null +++ b/ui/app/components/send_/send-footer/tests/send-footer-selectors.test.js @@ -0,0 +1,24 @@ +import assert from 'assert' +import proxyquire from 'proxyquire' + +const { + isSendFormInError, +} = proxyquire('../send-footer.selectors', { + '../send.selectors': { + getSendErrors: (mockState) => mockState.errors, + }, +}) + +describe('send-footer selectors', () => { + + describe('getTitleKey()', () => { + it('should return true if any of the values of the object returned by getSendErrors are truthy', () => { + assert.equal(isSendFormInError({ errors: { a: 'abc', b: false} }), true) + }) + + it('should return false if all of the values of the object returned by getSendErrors are falsy', () => { + assert.equal(isSendFormInError({ errors: { a: false, b: null} }), false) + }) + }) + +}) -- cgit From 0f20fce9b761fc0aa16d61b2b739fa7f9b9f6a7d Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 23 May 2018 14:13:25 -0230 Subject: Auto update gas estimate when to changes. --- ui/app/components/send_/send-footer/send-footer.utils.js | 1 - 1 file changed, 1 deletion(-) (limited to 'ui/app/components/send_/send-footer') diff --git a/ui/app/components/send_/send-footer/send-footer.utils.js b/ui/app/components/send_/send-footer/send-footer.utils.js index d5639629d..875e7d948 100644 --- a/ui/app/components/send_/send-footer/send-footer.utils.js +++ b/ui/app/components/send_/send-footer/send-footer.utils.js @@ -42,7 +42,6 @@ function constructUpdatedTx ({ } if (selectedToken) { - console.log(`ethAbi.rawEncode`, ethAbi.rawEncode) const data = TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call( ethAbi.rawEncode(['address', 'uint256'], [to, ethUtil.addHexPrefix(amount)]), x => ('00' + x.toString(16)).slice(-2) -- cgit