From 9ccc609e567b373b5f02bddc4d1095b2ce2d6c44 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 7 May 2018 08:03:20 -0400 Subject: [PATCH] Adds test for send, send-content, send-footer and send-header components. --- .../tests/send-content-component.test.js | 38 +++ .../send-footer/send-footer.component.js | 17 +- .../tests/send-footer-component.test.js | 159 ++++++++++++ .../send-header/send-header.component.js | 14 +- .../tests/send-header-component.test.js | 70 ++++++ .../send_/tests/send-component.test.js | 234 ++++++++++++++++++ 6 files changed, 516 insertions(+), 16 deletions(-) diff --git a/ui/app/components/send_/send-content/tests/send-content-component.test.js b/ui/app/components/send_/send-content/tests/send-content-component.test.js index e69de29bb..785545861 100644 --- a/ui/app/components/send_/send-content/tests/send-content-component.test.js +++ b/ui/app/components/send_/send-content/tests/send-content-component.test.js @@ -0,0 +1,38 @@ +import React from 'react' +import assert from 'assert' +import { shallow } from 'enzyme' +import SendContent from '../send-content.component.js' + +import PageContainerContent from '../../../page-container/page-container-content.component' +import SendAmountRow from '../send-amount-row/send-amount-row.container' +import SendFromRow from '../send-from-row/send-from-row.container' +import SendGasRow from '../send-gas-row/send-gas-row.container' +import SendToRow from '../send-to-row/send-to-row.container' + +describe('Send Component', function () { + let wrapper + + beforeEach(() => { + wrapper = shallow() + }) + + describe('render', () => { + it('should render a PageContainerContent component', () => { + assert.equal(wrapper.find(PageContainerContent).length, 1) + }) + + it('should render a div with a .send-v2__form class as a child of PageContainerContent', () => { + const PageContainerContentChild = wrapper.find(PageContainerContent).children() + PageContainerContentChild.is('div') + PageContainerContentChild.is('.send-v2__form') + }) + + it('should render the correct row components as grandchildren of the PageContainerContent component', () => { + const PageContainerContentChild = wrapper.find(PageContainerContent).children() + PageContainerContentChild.childAt(0).is(SendFromRow) + PageContainerContentChild.childAt(1).is(SendToRow) + PageContainerContentChild.childAt(2).is(SendAmountRow) + PageContainerContentChild.childAt(3).is(SendGasRow) + }) + }) +}) 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) + }) + }) +}) diff --git a/ui/app/components/send_/send-header/send-header.component.js b/ui/app/components/send_/send-header/send-header.component.js index dc4190b93..0d75dbdae 100644 --- a/ui/app/components/send_/send-header/send-header.component.js +++ b/ui/app/components/send_/send-header/send-header.component.js @@ -11,17 +11,17 @@ export default class SendHeader extends Component { isToken: PropTypes.bool, }; - render () { - const { isToken, clearSend, history } = this.props + onClose () { + this.props.clearSend() + this.props.history.push(DEFAULT_ROUTE) + } + render () { return ( { - clearSend() - history.push(DEFAULT_ROUTE) - }} + onClose={() => this.onClose()} subtitle={this.context.t('onlySendToEtherAddress')} - title={isToken ? this.context.t('sendTokens') : this.context.t('sendETH')} + title={this.props.isToken ? this.context.t('sendTokens') : this.context.t('sendETH')} /> ) } diff --git a/ui/app/components/send_/send-header/tests/send-header-component.test.js b/ui/app/components/send_/send-header/tests/send-header-component.test.js index e69de29bb..f1e387b72 100644 --- a/ui/app/components/send_/send-header/tests/send-header-component.test.js +++ b/ui/app/components/send_/send-header/tests/send-header-component.test.js @@ -0,0 +1,70 @@ +import React from 'react' +import assert from 'assert' +import { shallow } from 'enzyme' +import sinon from 'sinon' +import { DEFAULT_ROUTE } from '../../../../routes' +import SendHeader from '../send-header.component.js' + +import PageContainerHeader from '../../../page-container/page-container-header' + +const propsMethodSpies = { + clearSend: sinon.spy(), +} +const historySpies = { + push: sinon.spy(), +} + +sinon.spy(SendHeader.prototype, 'onClose') + +describe('Send Component', function () { + let wrapper + + beforeEach(() => { + wrapper = shallow(, { context: { t: str => str } }) + instance = wrapper.instance() + }) + + afterEach(() => { + propsMethodSpies.clearSend.resetHistory() + historySpies.push.resetHistory() + SendHeader.prototype.onClose.resetHistory() + }) + + describe('onClose', () => { + it('should call clearSend', () => { + assert.equal(propsMethodSpies.clearSend.callCount, 0) + wrapper.instance().onClose() + assert.equal(propsMethodSpies.clearSend.callCount, 1) + }) + + it('should call history.push', () => { + assert.equal(historySpies.push.callCount, 0) + wrapper.instance().onClose() + assert.equal(historySpies.push.callCount, 1) + assert.equal(historySpies.push.getCall(0).args[0], DEFAULT_ROUTE) + }) + }) + + describe('render', () => { + it('should render a PageContainerHeader compenent', () => { + assert.equal(wrapper.find(PageContainerHeader).length, 1) + }) + + it('should pass the correct props to PageContainerHeader', () => { + const { + onClose, + subtitle, + title, + } = wrapper.find(PageContainerHeader).props() + assert.equal(subtitle, 'onlySendToEtherAddress') + assert.equal(title, 'sendETH') + assert.equal(SendHeader.prototype.onClose.callCount, 0) + onClose() + assert.equal(SendHeader.prototype.onClose.callCount, 1) + }) + }) +}) diff --git a/ui/app/components/send_/tests/send-component.test.js b/ui/app/components/send_/tests/send-component.test.js index e69de29bb..eee0b23b8 100644 --- a/ui/app/components/send_/tests/send-component.test.js +++ b/ui/app/components/send_/tests/send-component.test.js @@ -0,0 +1,234 @@ +import React from 'react' +import assert from 'assert' +import proxyquire from 'proxyquire' +import { shallow } from 'enzyme' +import sinon from 'sinon' + +import SendHeader from '../send-header/send-header.container' +import SendContent from '../send-content/send-content.component' +import SendFooter from '../send-footer/send-footer.container' + +const propsMethodSpies = { + updateAndSetGasTotal: sinon.spy(), + updateSendErrors: sinon.spy(), + updateSendTokenBalance: sinon.spy(), +} +const utilsMethodStubs = { + getAmountErrorObject: sinon.stub().returns({ amount: 'mockAmountError' }), + doesAmountErrorRequireUpdate: sinon.stub().callsFake(obj => obj.balance !== obj.prevBalance), +} + +const SendTransactionScreen = proxyquire('../send.component.js', { + './send.utils': utilsMethodStubs, +}).default + +sinon.spy(SendTransactionScreen.prototype, 'componentDidMount') +sinon.spy(SendTransactionScreen.prototype, 'updateGas') + +describe('Send Component', function () { + let wrapper + + beforeEach(() => { + wrapper = shallow() + instance = wrapper.instance() + }) + + afterEach(() => { + SendTransactionScreen.prototype.componentDidMount.resetHistory() + SendTransactionScreen.prototype.updateGas.resetHistory() + utilsMethodStubs.doesAmountErrorRequireUpdate.resetHistory() + utilsMethodStubs.getAmountErrorObject.resetHistory() + propsMethodSpies.updateAndSetGasTotal.resetHistory() + propsMethodSpies.updateSendErrors.resetHistory() + propsMethodSpies.updateSendTokenBalance.resetHistory() + }) + + it('should call componentDidMount', () => { + assert(SendTransactionScreen.prototype.componentDidMount.calledOnce) + }) + + describe('componentWillMount', () => { + it('should call this.updateGas', () => { + assert(SendTransactionScreen.prototype.updateGas.calledOnce) + wrapper.instance().componentWillMount() + assert(SendTransactionScreen.prototype.updateGas.calledTwice) + }) + }) + + describe('componentDidUpdate', () => { + it('should call doesAmountErrorRequireUpdate with the expected params', () => { + wrapper.instance().componentDidUpdate({ + from: { + balance: '', + }, + }) + assert(utilsMethodStubs.doesAmountErrorRequireUpdate.calledOnce) + assert.deepEqual( + utilsMethodStubs.doesAmountErrorRequireUpdate.getCall(0).args[0], + { + balance: 'mockBalance', + gasTotal: 'mockGasTotal', + prevBalance: '', + prevGasTotal: undefined, + prevTokenBalance: undefined, + selectedToken: 'mockSelectedToken', + tokenBalance: 'mockTokenBalance', + } + ) + }) + + it('should not call getAmountErrorObject if doesAmountErrorRequireUpdate returns false', () => { + wrapper.instance().componentDidUpdate({ + from: { + balance: 'mockBalance', + }, + }) + assert.equal(utilsMethodStubs.getAmountErrorObject.callCount, 0) + }) + + it('should call getAmountErrorObject if doesAmountErrorRequireUpdate returns true', () => { + wrapper.instance().componentDidUpdate({ + from: { + balance: 'balanceChanged', + }, + }) + assert.equal(utilsMethodStubs.getAmountErrorObject.callCount, 1) + assert.deepEqual( + utilsMethodStubs.getAmountErrorObject.getCall(0).args[0], + { + amount: 'mockAmount', + amountConversionRate: 'mockAmountConversionRate', + balance: 'mockBalance', + conversionRate: 10, + gasTotal: 'mockGasTotal', + primaryCurrency: 'mockPrimaryCurrency', + selectedToken: 'mockSelectedToken', + tokenBalance: 'mockTokenBalance', + } + ) + }) + + it('should call updateSendErrors with the expected params', () => { + wrapper.instance().componentDidUpdate({ + from: { + balance: 'balanceChanged', + }, + }) + assert.equal(propsMethodSpies.updateSendErrors.callCount, 1) + assert.deepEqual( + propsMethodSpies.updateSendErrors.getCall(0).args[0], + { amount: 'mockAmountError'} + ) + }) + + it('should not call updateSendTokenBalance or this.updateGas if network === prevNetwork', () => { + SendTransactionScreen.prototype.updateGas.resetHistory() + wrapper.instance().componentDidUpdate({ + from: { + balance: 'balanceChanged', + }, + network: '3', + }) + assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 0) + assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0) + }) + + it('should not call updateSendTokenBalance or this.updateGas if network === loading', () => { + wrapper.setProps({ network: 'loading' }) + SendTransactionScreen.prototype.updateGas.resetHistory() + propsMethodSpies.updateSendTokenBalance.resetHistory() + wrapper.instance().componentDidUpdate({ + from: { + balance: 'balanceChanged', + }, + network: '3', + }) + assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 0) + assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0) + }) + + it('should call updateSendTokenBalance and this.updateGas with the correct params', () => { + SendTransactionScreen.prototype.updateGas.resetHistory() + wrapper.instance().componentDidUpdate({ + from: { + balance: 'balanceChanged', + }, + network: '2', + }) + assert.equal(propsMethodSpies.updateSendTokenBalance.callCount, 1) + assert.deepEqual( + propsMethodSpies.updateSendTokenBalance.getCall(0).args[0], + { + selectedToken: 'mockSelectedToken', + tokenContract: 'mockTokenContract', + address: 'mockAddress', + } + ) + assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 1) + assert.deepEqual( + SendTransactionScreen.prototype.updateGas.getCall(0).args, + [] + ) + }) + }) + + describe('updateGas', () => { + it('should call updateAndSetGasTotal with the correct params', () => { + propsMethodSpies.updateAndSetGasTotal.resetHistory() + wrapper.instance().updateGas() + assert.equal(propsMethodSpies.updateAndSetGasTotal.callCount, 1) + assert.deepEqual( + propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0], + { + data: 'mockData', + editingTransactionId: 'mockEditingTransactionId', + gasLimit: 'mockGasLimit', + gasPrice: 'mockGasPrice', + selectedAddress: 'mockSelectedAddress', + selectedToken: 'mockSelectedToken', + } + ) + }) + }) + + describe('render', () => { + it('should render a page-container class', () => { + assert.equal(wrapper.find('.page-container').length, 1) + }) + + it('should render SendHeader, SendContent and SendFooter', () => { + assert.equal(wrapper.find(SendHeader).length, 1) + assert.equal(wrapper.find(SendContent).length, 1) + assert.equal(wrapper.find(SendFooter).length, 1) + }) + + it('should pass the history prop to SendHeader and SendFooter', () => { + assert.deepEqual( + wrapper.find(SendFooter).props(), + { + history: { mockProp: 'history-abc' }, + } + ) + }) + }) +})