diff options
author | Thomas Huang <tmashuang@users.noreply.github.com> | 2018-12-12 06:56:47 +0800 |
---|---|---|
committer | Dan Finlay <542863+danfinlay@users.noreply.github.com> | 2018-12-12 06:56:47 +0800 |
commit | 2f5abd9ad8ac64c16edcc0d9fff923e62c5ca984 (patch) | |
tree | 16d323cd3325b1b9121434e86c3047dc333c2b1d /ui/app/components/send | |
parent | ed9bfdcebd5eed1d749f275f9d388ea0dd8f8275 (diff) | |
download | tangerine-wallet-browser-5.2.0.tar tangerine-wallet-browser-5.2.0.tar.gz tangerine-wallet-browser-5.2.0.tar.bz2 tangerine-wallet-browser-5.2.0.tar.lz tangerine-wallet-browser-5.2.0.tar.xz tangerine-wallet-browser-5.2.0.tar.zst tangerine-wallet-browser-5.2.0.zip |
Version Bump (#5909)v5.2.0
* Adds new gas customization modal container (without content)
* Adds the content of the advanced tab - w/o chart or dynamic content - to gas customize modal.
* Use correct message key in gas-modal-page-container.component.js
* Use BEM for css in gas-modal-page-container
* Split advanced-tab-content.component.js render() method into smaller pieces; add translations to the same file.
* Remove gas slider from advance-tab-content.component
* Add tests for advanced-tab-component.js and subcomponents.
* Improve styling of advanced-tab-content gasInput row
* Adds basic tab content to gas customizer, with styled button group (static, for now).
* Connect the gas-button-group component to redux and a live api.
* Improvements to propdefaults in button-group.component and basic-tab-content.component
* Styling fixes for gas customization advanced tab content.
* Adds gas-duck.test.js tests.
* Connects remained of the gas customization component to redux.
* Integrate gas buttons with the send screen.
* Test updates and additions for button integration with send screen.
* Adds redesign for the customize gas advanced tab.
* Adds not yet functional gas price chart.
* Gas price chart improvements, redesign, bug fixes, and set up to receive external data
* Read only connection of gas price chart to redux
* Clean up for advanced gas tab customization changes.
* Complete integration of gas chart with redux.
* Add control arrows to advanced gas tab inputs.
* Lint and unit test fixes.
* Clean up gas chart code.
* Update tests, plus some lint fixes, for gas-price-chart
* Improve data management and tests for gas-modal-page-container price estimates.
* Clean up for mmui-i11-custom-gas-price-chart branch
* Redesign of gas customization basic tab.
* Adds createSpeedUpTransaction to txController
* Connect gas price chart to gas station api.
* Adds speed up slide-in gas customization sidebar
* Update e2e tests for new gas customization modal.
* Fixes for components that break e2e gas customization tests, plus unit test updates.
* Remove gas customization integration tests (in favour of e2e tests)
* Add gas data to integration test json data set.
* Add c3 and d3 to the separate dependencies bundle.
* Make gas customization modal responsive.
* Fix "fastest" translation message; change to sentence case
* Uses more reliable api on main send screen; caches basic api results in modal
* Add loading spinners when waiting for APIs in the gas customization modal
* Modify results of API data to better fit gas chart: remove outliers, pad data
* Clear custom gas data on hiding of gas customization modal.
* Improve responsiveness of customize speed up slider.
* Final gas customization fixes
* Fix styling of send screen in extension view when hex data on.
* Replace height: 100% rule with workaround for flexbox quirks
* Fill in more Polish message translations
* Update lockfile to fix errors
npm has informed me that the lockfile has "errors":
npm ERR! code ELOCKVERIFY
npm ERR! Errors were found in your package-lock.json, run npm install to fix them.
npm ERR! Missing: c3@^0.6.7
npm ERR! Invalid: lock file's d3@3.5.17 does not satisfy d3@^5.7.0
* circleci: Disable npm audit when installing packages
Auditing packages when installing here doesn't help anyone as the summary
isn't visible and vulnerabilities don't produce a non-zero exit code. We
will have `npm audit` as an extra CI job.
* npm audit fix
* circleci: Replace nsp with npm audit
Refs #4751
* Remove beefy dependency and its usages
Refs #4768
Refs #5389
This changeset removes the beefy package that:
1. Was last published 2 yrs ago
2. Brought with it 1 moderate and 1 critical vulnerability
3. Was only used in scripts that no longer work
* npm uninstall open
* Update ganache-core to mitigate vuln
=== npm audit security report ===
> # Run npm install --save-dev ganache-core@2.3.1 to resolve 1 vulnerability
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ Moderate │ Memory Exposure │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ bl │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ ganache-core [dev] │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ ganache-core > level-sublevel > levelup > bl │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://nodesecurity.io/advisories/596 │
└───────────────┴──────────────────────────────────────────────────────────────┘
* Deduplicate package.json file
From `npm install`:
> npm WARN The package css-loader is included as both a dev and production dependency.
> npm WARN The package eslint-plugin-react is included as both a dev and production dependency.
> npm WARN The package file-loader is included as both a dev and production dependency.
> npm WARN The package gulp is included as both a dev and production dependency.
It's also worth noting that the Gulp version we were using was inconsistent and there is
a published v4 release on GitHub.
* Fix race condition in network controller lookup() method.
* Group transactions by nonce (#5886)
* fix formatting of 32-byte strings in personal_sign (#5878)
* Bump json-rpc-engine to v4.0.0
* Bump package lock, mostly to https links
* Improve ux for low gas price set (#5862)
* Show user warning if they set gas price below safelow minimum, error if 0.
* Properly cache basic price estimate data.
* Default retry price to recommended price if original price was 0x0
* Use mock fetch in send-new-ui integration tests.
* Show Failed transaction in the browser notification for on-chain failures (#5904)
* Changelog and version bump for 5.2.0
Diffstat (limited to 'ui/app/components/send')
14 files changed, 313 insertions, 59 deletions
diff --git a/ui/app/components/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js index 9bbb67506..b667aa037 100644 --- a/ui/app/components/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js +++ b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js @@ -11,7 +11,7 @@ export default class GasFeeDisplay extends Component { convertedCurrency: PropTypes.string, gasLoadingError: PropTypes.bool, gasTotal: PropTypes.string, - onClick: PropTypes.func, + onReset: PropTypes.func, }; static contextTypes = { @@ -19,7 +19,7 @@ export default class GasFeeDisplay extends Component { }; render () { - const { gasTotal, onClick, gasLoadingError } = this.props + const { gasTotal, gasLoadingError, onReset } = this.props return ( <div className="send-v2__gas-fee-display"> @@ -46,11 +46,10 @@ export default class GasFeeDisplay extends Component { </div> } <button - className="sliders-icon-container" - onClick={onClick} - disabled={!gasTotal && !gasLoadingError} + className="gas-fee-reset" + onClick={onReset} > - <i className="fa fa-sliders sliders-icon" /> + { this.context.t('reset') } </button> </div> ) diff --git a/ui/app/components/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js index 9ff01493a..cb4180508 100644 --- a/ui/app/components/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js +++ b/ui/app/components/send/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js @@ -8,18 +8,20 @@ import sinon from 'sinon' const propsMethodSpies = { showCustomizeGasModal: sinon.spy(), + onReset: sinon.spy(), } -describe('SendGasRow Component', function () { +describe('GasFeeDisplay Component', function () { let wrapper beforeEach(() => { wrapper = shallow(<GasFeeDisplay conversionRate={20} gasTotal={'mockGasTotal'} - onClick={propsMethodSpies.showCustomizeGasModal} primaryCurrency={'mockPrimaryCurrency'} convertedCurrency={'mockConvertedCurrency'} + showGasButtonGroup={propsMethodSpies.showCustomizeGasModal} + onReset={propsMethodSpies.onReset} />, {context: {t: str => str + '_t'}}) }) @@ -41,13 +43,19 @@ describe('SendGasRow Component', function () { assert.equal(value, 'mockGasTotal') }) - it('should render the Button with the correct props', () => { + it('should render the reset button with the correct props', () => { const { onClick, + className, } = wrapper.find('button').props() - assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 0) + assert.equal(className, 'gas-fee-reset') + assert.equal(propsMethodSpies.onReset.callCount, 0) onClick() - assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 1) + assert.equal(propsMethodSpies.onReset.callCount, 1) + }) + + it('should render the reset button with the correct text', () => { + assert.equal(wrapper.find('button').text(), 'reset_t') }) }) }) diff --git a/ui/app/components/send/send-content/send-gas-row/send-gas-row.component.js b/ui/app/components/send/send-content/send-gas-row/send-gas-row.component.js index 6ad4499ff..8d305dd4f 100644 --- a/ui/app/components/send/send-content/send-gas-row/send-gas-row.component.js +++ b/ui/app/components/send/send-content/send-gas-row/send-gas-row.component.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import SendRowWrapper from '../send-row-wrapper/' import GasFeeDisplay from './gas-fee-display/gas-fee-display.component' +import GasPriceButtonGroup from '../../../gas-customization/gas-price-button-group' export default class SendGasRow extends Component { @@ -12,6 +13,9 @@ export default class SendGasRow extends Component { gasLoadingError: PropTypes.bool, gasTotal: PropTypes.string, showCustomizeGasModal: PropTypes.func, + gasPriceButtonGroupProps: PropTypes.object, + gasButtonGroupShown: PropTypes.bool, + resetGasButtons: PropTypes.func, } static contextTypes = { @@ -26,21 +30,37 @@ export default class SendGasRow extends Component { gasTotal, gasFeeError, showCustomizeGasModal, + gasPriceButtonGroupProps, + gasButtonGroupShown, + resetGasButtons, } = this.props return ( <SendRowWrapper - label={`${this.context.t('gasFee')}:`} + label={`${this.context.t('transactionFee')}:`} showError={gasFeeError} errorType={'gasFee'} > - <GasFeeDisplay - conversionRate={conversionRate} - convertedCurrency={convertedCurrency} - gasLoadingError={gasLoadingError} - gasTotal={gasTotal} - onClick={() => showCustomizeGasModal()} - /> + {gasButtonGroupShown + ? <div> + <GasPriceButtonGroup + className="gas-price-button-group--small" + showCheck={false} + {...gasPriceButtonGroupProps} + /> + <div className="advanced-gas-options-btn" onClick={() => showCustomizeGasModal()}> + { this.context.t('advancedOptions') } + </div> + </div> + : <GasFeeDisplay + conversionRate={conversionRate} + convertedCurrency={convertedCurrency} + gasLoadingError={gasLoadingError} + gasTotal={gasTotal} + onReset={resetGasButtons} + onClick={() => showCustomizeGasModal()} + />} + </SendRowWrapper> ) } diff --git a/ui/app/components/send/send-content/send-gas-row/send-gas-row.container.js b/ui/app/components/send/send-content/send-gas-row/send-gas-row.container.js index e44fe4ef1..977f8ab3c 100644 --- a/ui/app/components/send/send-content/send-gas-row/send-gas-row.container.js +++ b/ui/app/components/send/send-content/send-gas-row/send-gas-row.container.js @@ -3,25 +3,76 @@ import { getConversionRate, getCurrentCurrency, getGasTotal, + getGasPrice, } from '../../send.selectors.js' -import { getGasLoadingError, gasFeeIsInError } from './send-gas-row.selectors.js' -import { showModal } from '../../../../actions' +import { + getBasicGasEstimateLoadingStatus, + getRenderableEstimateDataForSmallButtonsFromGWEI, + getDefaultActiveButtonIndex, +} from '../../../../selectors/custom-gas' +import { + showGasButtonGroup, +} from '../../../../ducks/send.duck' +import { + resetCustomData, +} from '../../../../ducks/gas.duck' +import { getGasLoadingError, gasFeeIsInError, getGasButtonGroupShown } from './send-gas-row.selectors.js' +import { showModal, setGasPrice } from '../../../../actions' import SendGasRow from './send-gas-row.component' -export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow) +export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(SendGasRow) function mapStateToProps (state) { + const gasButtonInfo = getRenderableEstimateDataForSmallButtonsFromGWEI(state) + const activeButtonIndex = getDefaultActiveButtonIndex(gasButtonInfo, getGasPrice(state)) + return { conversionRate: getConversionRate(state), convertedCurrency: getCurrentCurrency(state), gasTotal: getGasTotal(state), gasFeeError: gasFeeIsInError(state), gasLoadingError: getGasLoadingError(state), + gasPriceButtonGroupProps: { + buttonDataLoading: getBasicGasEstimateLoadingStatus(state), + defaultActiveButtonIndex: 1, + newActiveButtonIndex: activeButtonIndex > -1 ? activeButtonIndex : null, + gasButtonInfo, + }, + gasButtonGroupShown: getGasButtonGroupShown(state), } } function mapDispatchToProps (dispatch) { return { - showCustomizeGasModal: () => dispatch(showModal({ name: 'CUSTOMIZE_GAS' })), + showCustomizeGasModal: () => dispatch(showModal({ name: 'CUSTOMIZE_GAS', hideBasic: true })), + setGasPrice: newPrice => dispatch(setGasPrice(newPrice)), + showGasButtonGroup: () => dispatch(showGasButtonGroup()), + resetCustomData: () => dispatch(resetCustomData()), + } +} + +function mergeProps (stateProps, dispatchProps, ownProps) { + const { gasPriceButtonGroupProps } = stateProps + const { gasButtonInfo } = gasPriceButtonGroupProps + const { + setGasPrice: dispatchSetGasPrice, + showGasButtonGroup: dispatchShowGasButtonGroup, + resetCustomData: dispatchResetCustomData, + ...otherDispatchProps + } = dispatchProps + + return { + ...stateProps, + ...otherDispatchProps, + ...ownProps, + gasPriceButtonGroupProps: { + ...gasPriceButtonGroupProps, + handleGasPriceSelection: dispatchSetGasPrice, + }, + resetGasButtons: () => { + dispatchResetCustomData() + dispatchSetGasPrice(gasButtonInfo[1].priceInHexWei) + dispatchShowGasButtonGroup() + }, } } diff --git a/ui/app/components/send/send-content/send-gas-row/send-gas-row.selectors.js b/ui/app/components/send/send-content/send-gas-row/send-gas-row.selectors.js index 96f6293c2..79c838543 100644 --- a/ui/app/components/send/send-content/send-gas-row/send-gas-row.selectors.js +++ b/ui/app/components/send/send-content/send-gas-row/send-gas-row.selectors.js @@ -1,6 +1,7 @@ const selectors = { gasFeeIsInError, getGasLoadingError, + getGasButtonGroupShown, } module.exports = selectors @@ -12,3 +13,7 @@ function getGasLoadingError (state) { function gasFeeIsInError (state) { return Boolean(state.send.errors.gasFee) } + +function getGasButtonGroupShown (state) { + return state.send.gasButtonGroupShown +} diff --git a/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-component.test.js b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-component.test.js index 54a92bd2d..059c6cdd3 100644 --- a/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-component.test.js +++ b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-component.test.js @@ -6,9 +6,11 @@ import SendGasRow from '../send-gas-row.component.js' import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component' import GasFeeDisplay from '../gas-fee-display/gas-fee-display.component' +import GasPriceButtonGroup from '../../../../gas-customization/gas-price-button-group' const propsMethodSpies = { showCustomizeGasModal: sinon.spy(), + resetGasButtons: sinon.spy(), } describe('SendGasRow Component', function () { @@ -21,12 +23,18 @@ describe('SendGasRow Component', function () { gasFeeError={'mockGasFeeError'} gasLoadingError={false} gasTotal={'mockGasTotal'} + gasButtonGroupShown={false} showCustomizeGasModal={propsMethodSpies.showCustomizeGasModal} + resetGasButtons={propsMethodSpies.resetGasButtons} + gasPriceButtonGroupProps={{ + someGasPriceButtonGroupProp: 'foo', + anotherGasPriceButtonGroupProp: 'bar', + }} />, { context: { t: str => str + '_t' } }) }) afterEach(() => { - propsMethodSpies.showCustomizeGasModal.resetHistory() + propsMethodSpies.resetGasButtons.resetHistory() }) describe('render', () => { @@ -41,7 +49,7 @@ describe('SendGasRow Component', function () { errorType, } = wrapper.find(SendRowWrapper).props() - assert.equal(label, 'gasFee_t:') + assert.equal(label, 'transactionFee_t:') assert.equal(showError, 'mockGasFeeError') assert.equal(errorType, 'gasFee') }) @@ -56,14 +64,40 @@ describe('SendGasRow Component', function () { convertedCurrency, gasLoadingError, gasTotal, - onClick, + onReset, } = wrapper.find(SendRowWrapper).childAt(0).props() assert.equal(conversionRate, 20) assert.equal(convertedCurrency, 'mockConvertedCurrency') assert.equal(gasLoadingError, false) assert.equal(gasTotal, 'mockGasTotal') + assert.equal(propsMethodSpies.resetGasButtons.callCount, 0) + onReset() + assert.equal(propsMethodSpies.resetGasButtons.callCount, 1) + }) + + it('should render the GasPriceButtonGroup if gasButtonGroupShown is true', () => { + wrapper.setProps({ gasButtonGroupShown: true }) + const rendered = wrapper.find(SendRowWrapper).childAt(0) + assert.equal(rendered.children().length, 2) + + const gasPriceButtonGroup = rendered.childAt(0) + assert(gasPriceButtonGroup.is(GasPriceButtonGroup)) + assert(gasPriceButtonGroup.hasClass('gas-price-button-group--small')) + assert.equal(gasPriceButtonGroup.props().showCheck, false) + assert.equal(gasPriceButtonGroup.props().someGasPriceButtonGroupProp, 'foo') + assert.equal(gasPriceButtonGroup.props().anotherGasPriceButtonGroupProp, 'bar') + }) + + it('should render an advanced options button if gasButtonGroupShown is true', () => { + wrapper.setProps({ gasButtonGroupShown: true }) + const rendered = wrapper.find(SendRowWrapper).childAt(0) + assert.equal(rendered.children().length, 2) + + const advancedOptionsButton = rendered.childAt(1) + assert.equal(advancedOptionsButton.text(), 'advancedOptions_t') + assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 0) - onClick() + advancedOptionsButton.props().onClick() assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 1) }) }) diff --git a/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-container.test.js index 2ce062505..f0c82e4f7 100644 --- a/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-container.test.js +++ b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-container.test.js @@ -4,16 +4,27 @@ import sinon from 'sinon' let mapStateToProps let mapDispatchToProps +let mergeProps const actionSpies = { showModal: sinon.spy(), + setGasPrice: sinon.spy(), +} + +const sendDuckSpies = { + showGasButtonGroup: sinon.spy(), +} + +const gasDuckSpies = { + resetCustomData: sinon.spy(), } proxyquire('../send-gas-row.container.js', { 'react-redux': { - connect: (ms, md) => { + connect: (ms, md, mp) => { mapStateToProps = ms mapDispatchToProps = md + mergeProps = mp return () => ({}) }, }, @@ -21,12 +32,21 @@ proxyquire('../send-gas-row.container.js', { getConversionRate: (s) => `mockConversionRate:${s}`, getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`, getGasTotal: (s) => `mockGasTotal:${s}`, + getGasPrice: (s) => `mockGasPrice:${s}`, }, './send-gas-row.selectors.js': { getGasLoadingError: (s) => `mockGasLoadingError:${s}`, gasFeeIsInError: (s) => `mockGasFeeError:${s}`, + getGasButtonGroupShown: (s) => `mockGetGasButtonGroupShown:${s}`, }, '../../../../actions': actionSpies, + '../../../../selectors/custom-gas': { + getBasicGasEstimateLoadingStatus: (s) => `mockBasicGasEstimateLoadingStatus:${s}`, + getRenderableEstimateDataForSmallButtonsFromGWEI: (s) => `mockGasButtonInfo:${s}`, + getDefaultActiveButtonIndex: (gasButtonInfo, gasPrice) => gasButtonInfo.length + gasPrice.length, + }, + '../../../../ducks/send.duck': sendDuckSpies, + '../../../../ducks/gas.duck': gasDuckSpies, }) describe('send-gas-row container', () => { @@ -40,6 +60,13 @@ describe('send-gas-row container', () => { gasTotal: 'mockGasTotal:mockState', gasFeeError: 'mockGasFeeError:mockState', gasLoadingError: 'mockGasLoadingError:mockState', + gasPriceButtonGroupProps: { + buttonDataLoading: `mockBasicGasEstimateLoadingStatus:mockState`, + defaultActiveButtonIndex: 1, + newActiveButtonIndex: 49, + gasButtonInfo: `mockGasButtonInfo:mockState`, + }, + gasButtonGroupShown: `mockGetGasButtonGroupShown:mockState`, }) }) @@ -60,11 +87,74 @@ describe('send-gas-row container', () => { assert(dispatchSpy.calledOnce) assert.deepEqual( actionSpies.showModal.getCall(0).args[0], - { name: 'CUSTOMIZE_GAS' } + { name: 'CUSTOMIZE_GAS', hideBasic: true } ) }) }) + describe('setGasPrice()', () => { + it('should dispatch an action', () => { + mapDispatchToPropsObject.setGasPrice('mockNewPrice') + assert(dispatchSpy.calledOnce) + assert(actionSpies.setGasPrice.calledOnce) + assert.equal(actionSpies.setGasPrice.getCall(0).args[0], 'mockNewPrice') + }) + }) + + describe('showGasButtonGroup()', () => { + it('should dispatch an action', () => { + mapDispatchToPropsObject.showGasButtonGroup() + assert(dispatchSpy.calledOnce) + assert(sendDuckSpies.showGasButtonGroup.calledOnce) + }) + }) + + describe('resetCustomData()', () => { + it('should dispatch an action', () => { + mapDispatchToPropsObject.resetCustomData() + assert(dispatchSpy.calledOnce) + assert(gasDuckSpies.resetCustomData.calledOnce) + }) + }) + + }) + + describe('mergeProps', () => { + let stateProps + let dispatchProps + let ownProps + + beforeEach(() => { + stateProps = { + gasPriceButtonGroupProps: { + someGasPriceButtonGroupProp: 'foo', + anotherGasPriceButtonGroupProp: 'bar', + }, + someOtherStateProp: 'baz', + } + dispatchProps = { + setGasPrice: sinon.spy(), + someOtherDispatchProp: sinon.spy(), + } + ownProps = { someOwnProp: 123 } + }) + + it('should return the expected props when isConfirm is true', () => { + const result = mergeProps(stateProps, dispatchProps, ownProps) + + assert.equal(result.someOtherStateProp, 'baz') + assert.equal(result.gasPriceButtonGroupProps.someGasPriceButtonGroupProp, 'foo') + assert.equal(result.gasPriceButtonGroupProps.anotherGasPriceButtonGroupProp, 'bar') + assert.equal(result.someOwnProp, 123) + + assert.equal(dispatchProps.setGasPrice.callCount, 0) + result.gasPriceButtonGroupProps.handleGasPriceSelection() + assert.equal(dispatchProps.setGasPrice.callCount, 1) + + assert.equal(dispatchProps.someOtherDispatchProp.callCount, 0) + result.someOtherDispatchProp() + assert.equal(dispatchProps.someOtherDispatchProp.callCount, 1) + }) }) }) diff --git a/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js index d46dd9d8b..bd3c9a257 100644 --- a/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js +++ b/ui/app/components/send/send-content/send-gas-row/tests/send-gas-row-selectors.test.js @@ -2,6 +2,7 @@ import assert from 'assert' import { gasFeeIsInError, getGasLoadingError, + getGasButtonGroupShown, } from '../send-gas-row.selectors.js' describe('send-gas-row selectors', () => { @@ -46,4 +47,16 @@ describe('send-gas-row selectors', () => { }) }) + describe('getGasButtonGroupShown()', () => { + it('should return send.gasButtonGroupShown', () => { + const state = { + send: { + gasButtonGroupShown: 'foobar', + }, + } + + assert.equal(getGasButtonGroupShown(state), 'foobar') + }) + }) + }) diff --git a/ui/app/components/send/send.component.js b/ui/app/components/send/send.component.js index a27401f30..9b512aaf6 100644 --- a/ui/app/components/send/send.component.js +++ b/ui/app/components/send/send.component.js @@ -35,6 +35,7 @@ export default class SendTransactionScreen extends PersistentForm { selectedToken: PropTypes.object, tokenBalance: PropTypes.string, tokenContract: PropTypes.object, + fetchBasicGasEstimates: PropTypes.func, updateAndSetGasTotal: PropTypes.func, updateSendErrors: PropTypes.func, updateSendTokenBalance: PropTypes.func, @@ -73,10 +74,10 @@ export default class SendTransactionScreen extends PersistentForm { selectedAddress, selectedToken = {}, to: currentToAddress, - updateAndSetGasTotal, + updateAndSetGasLimit, } = this.props - updateAndSetGasTotal({ + updateAndSetGasLimit({ blockGasLimit, editingTransactionId, gasLimit, @@ -162,6 +163,13 @@ export default class SendTransactionScreen extends PersistentForm { } } + componentDidMount () { + this.props.fetchBasicGasEstimates() + .then(() => { + this.updateGas() + }) + } + componentWillMount () { const { from: { address }, @@ -169,12 +177,12 @@ export default class SendTransactionScreen extends PersistentForm { tokenContract, updateSendTokenBalance, } = this.props + updateSendTokenBalance({ selectedToken, tokenContract, address, }) - this.updateGas() // Show QR Scanner modal if ?scan=true if (window.location.search === '?scan=true') { diff --git a/ui/app/components/send/send.container.js b/ui/app/components/send/send.container.js index 87056499f..402e4bbe5 100644 --- a/ui/app/components/send/send.container.js +++ b/ui/app/components/send/send.container.js @@ -37,6 +37,9 @@ import { updateSendErrors, } from '../../ducks/send.duck' import { + fetchBasicGasEstimates, +} from '../../ducks/gas.duck' +import { calcGasTotal, } from './send.utils.js' @@ -76,7 +79,7 @@ function mapStateToProps (state) { function mapDispatchToProps (dispatch) { return { - updateAndSetGasTotal: ({ + updateAndSetGasLimit: ({ blockGasLimit, editingTransactionId, gasLimit, @@ -89,7 +92,7 @@ function mapDispatchToProps (dispatch) { data, }) => { !editingTransactionId - ? dispatch(updateGasData({ recentBlocks, selectedAddress, selectedToken, blockGasLimit, to, value, data })) + ? dispatch(updateGasData({ gasPrice, recentBlocks, selectedAddress, selectedToken, blockGasLimit, to, value, data })) : dispatch(setGasTotal(calcGasTotal(gasLimit, gasPrice))) }, updateSendTokenBalance: ({ selectedToken, tokenContract, address }) => { @@ -104,5 +107,6 @@ function mapDispatchToProps (dispatch) { scanQrCode: () => dispatch(showQrScanner(SEND_ROUTE)), qrCodeDetected: (data) => dispatch(qrCodeDetected(data)), updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)), + fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()), } } diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index c217d848e..443c82af5 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -8,7 +8,11 @@ const { } = require('../../selectors') const { estimateGasPriceFromRecentBlocks, + calcGasTotal, } = require('./send.utils') +import { + getFastPriceEstimateInHexWEI, +} from '../../selectors/custom-gas' const selectors = { accountsWithSendEtherInfoSelector, @@ -131,11 +135,11 @@ function getForceGasMin (state) { } function getGasLimit (state) { - return state.metamask.send.gasLimit + return state.metamask.send.gasLimit || '0' } function getGasPrice (state) { - return state.metamask.send.gasPrice + return state.metamask.send.gasPrice || getFastPriceEstimateInHexWEI(state) } function getGasPriceFromRecentBlocks (state) { @@ -143,7 +147,7 @@ function getGasPriceFromRecentBlocks (state) { } function getGasTotal (state) { - return state.metamask.send.gasTotal + return calcGasTotal(getGasLimit(state), getGasPrice(state)) } function getPrimaryCurrency (state) { diff --git a/ui/app/components/send/tests/send-component.test.js b/ui/app/components/send/tests/send-component.test.js index bd136a0b8..81955cc1d 100644 --- a/ui/app/components/send/tests/send-component.test.js +++ b/ui/app/components/send/tests/send-component.test.js @@ -3,16 +3,23 @@ import assert from 'assert' import proxyquire from 'proxyquire' import { shallow } from 'enzyme' import sinon from 'sinon' +import timeout from '../../../../lib/test-timeout' 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 mockBasicGasEstimates = { + blockTime: 'mockBlockTime', +} + const propsMethodSpies = { - updateAndSetGasTotal: sinon.spy(), + updateAndSetGasLimit: sinon.spy(), updateSendErrors: sinon.spy(), updateSendTokenBalance: sinon.spy(), resetSendState: sinon.spy(), + fetchBasicGasEstimates: sinon.stub().returns(Promise.resolve(mockBasicGasEstimates)), + fetchGasEstimates: sinon.spy(), } const utilsMethodStubs = { getAmountErrorObject: sinon.stub().returns({ amount: 'mockAmountError' }), @@ -37,6 +44,8 @@ describe('Send Component', function () { blockGasLimit={'mockBlockGasLimit'} conversionRate={10} editingTransactionId={'mockEditingTransactionId'} + fetchBasicGasEstimates={propsMethodSpies.fetchBasicGasEstimates} + fetchGasEstimates={propsMethodSpies.fetchGasEstimates} from={ { address: 'mockAddress', balance: 'mockBalance' } } gasLimit={'mockGasLimit'} gasPrice={'mockGasPrice'} @@ -50,7 +59,7 @@ describe('Send Component', function () { showHexData={true} tokenBalance={'mockTokenBalance'} tokenContract={'mockTokenContract'} - updateAndSetGasTotal={propsMethodSpies.updateAndSetGasTotal} + updateAndSetGasLimit={propsMethodSpies.updateAndSetGasLimit} updateSendErrors={propsMethodSpies.updateSendErrors} updateSendTokenBalance={propsMethodSpies.updateSendTokenBalance} resetSendState={propsMethodSpies.resetSendState} @@ -63,7 +72,8 @@ describe('Send Component', function () { utilsMethodStubs.doesAmountErrorRequireUpdate.resetHistory() utilsMethodStubs.getAmountErrorObject.resetHistory() utilsMethodStubs.getGasFeeErrorObject.resetHistory() - propsMethodSpies.updateAndSetGasTotal.resetHistory() + propsMethodSpies.fetchBasicGasEstimates.resetHistory() + propsMethodSpies.updateAndSetGasLimit.resetHistory() propsMethodSpies.updateSendErrors.resetHistory() propsMethodSpies.updateSendTokenBalance.resetHistory() }) @@ -72,12 +82,20 @@ describe('Send Component', function () { assert(SendTransactionScreen.prototype.componentDidMount.calledOnce) }) - describe('componentWillMount', () => { - it('should call this.updateGas', () => { + describe('componentDidMount', () => { + it('should call props.fetchBasicGasAndTimeEstimates', () => { + propsMethodSpies.fetchBasicGasEstimates.resetHistory() + assert.equal(propsMethodSpies.fetchBasicGasEstimates.callCount, 0) + wrapper.instance().componentDidMount() + assert.equal(propsMethodSpies.fetchBasicGasEstimates.callCount, 1) + }) + + it('should call this.updateGas', async () => { SendTransactionScreen.prototype.updateGas.resetHistory() propsMethodSpies.updateSendErrors.resetHistory() assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0) - wrapper.instance().componentWillMount() + wrapper.instance().componentDidMount() + await timeout(250) assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 1) }) }) @@ -271,12 +289,12 @@ describe('Send Component', function () { }) describe('updateGas', () => { - it('should call updateAndSetGasTotal with the correct params if no to prop is passed', () => { - propsMethodSpies.updateAndSetGasTotal.resetHistory() + it('should call updateAndSetGasLimit with the correct params if no to prop is passed', () => { + propsMethodSpies.updateAndSetGasLimit.resetHistory() wrapper.instance().updateGas() - assert.equal(propsMethodSpies.updateAndSetGasTotal.callCount, 1) + assert.equal(propsMethodSpies.updateAndSetGasLimit.callCount, 1) assert.deepEqual( - propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0], + propsMethodSpies.updateAndSetGasLimit.getCall(0).args[0], { blockGasLimit: 'mockBlockGasLimit', editingTransactionId: 'mockEditingTransactionId', @@ -292,20 +310,20 @@ describe('Send Component', function () { ) }) - it('should call updateAndSetGasTotal with the correct params if a to prop is passed', () => { - propsMethodSpies.updateAndSetGasTotal.resetHistory() + it('should call updateAndSetGasLimit with the correct params if a to prop is passed', () => { + propsMethodSpies.updateAndSetGasLimit.resetHistory() wrapper.setProps({ to: 'someAddress' }) wrapper.instance().updateGas() assert.equal( - propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0].to, + propsMethodSpies.updateAndSetGasLimit.getCall(0).args[0].to, 'someaddress', ) }) - it('should call updateAndSetGasTotal with to set to lowercase if passed', () => { - propsMethodSpies.updateAndSetGasTotal.resetHistory() + it('should call updateAndSetGasLimit with to set to lowercase if passed', () => { + propsMethodSpies.updateAndSetGasLimit.resetHistory() wrapper.instance().updateGas({ to: '0xABC' }) - assert.equal(propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0].to, '0xabc') + assert.equal(propsMethodSpies.updateAndSetGasLimit.getCall(0).args[0].to, '0xabc') }) }) diff --git a/ui/app/components/send/tests/send-container.test.js b/ui/app/components/send/tests/send-container.test.js index 6aa4bf826..19b6563e6 100644 --- a/ui/app/components/send/tests/send-container.test.js +++ b/ui/app/components/send/tests/send-container.test.js @@ -94,7 +94,7 @@ describe('send container', () => { mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy) }) - describe('updateAndSetGasTotal()', () => { + describe('updateAndSetGasLimit()', () => { const mockProps = { blockGasLimit: 'mockBlockGasLimit', editingTransactionId: '0x2', @@ -109,7 +109,7 @@ describe('send container', () => { } it('should dispatch a setGasTotal action when editingTransactionId is truthy', () => { - mapDispatchToPropsObject.updateAndSetGasTotal(mockProps) + mapDispatchToPropsObject.updateAndSetGasLimit(mockProps) assert(dispatchSpy.calledOnce) assert.equal( actionSpies.setGasTotal.getCall(0).args[0], @@ -118,14 +118,14 @@ describe('send container', () => { }) it('should dispatch an updateGasData action when editingTransactionId is falsy', () => { - const { selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, data } = mockProps - mapDispatchToPropsObject.updateAndSetGasTotal( + const { gasPrice, selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, data } = mockProps + mapDispatchToPropsObject.updateAndSetGasLimit( Object.assign({}, mockProps, {editingTransactionId: false}) ) assert(dispatchSpy.calledOnce) assert.deepEqual( actionSpies.updateGasData.getCall(0).args[0], - { selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, data } + { gasPrice, selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, data } ) }) }) diff --git a/ui/app/components/send/tests/send-selectors.test.js b/ui/app/components/send/tests/send-selectors.test.js index e7e901f0d..cdc86fe59 100644 --- a/ui/app/components/send/tests/send-selectors.test.js +++ b/ui/app/components/send/tests/send-selectors.test.js @@ -237,7 +237,7 @@ describe('send selectors', () => { it('should return the send.gasTotal', () => { assert.equal( getGasTotal(mockState), - '0xb451dc41b578' + 'a9ff56' ) }) }) |