Prevent users from changing the From field in the send screen (#5922)
* Prevent users from changing the From field in the send screen * Fix integration testsfeature/default_network_editable
parent
435fdae84a
commit
30a2be85ee
@ -1,46 +0,0 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import AccountListItem from '../../../account-list-item/' |
||||
import SendDropdownList from '../../send-dropdown-list/' |
||||
|
||||
export default class FromDropdown extends Component { |
||||
|
||||
static propTypes = { |
||||
accounts: PropTypes.array, |
||||
closeDropdown: PropTypes.func, |
||||
dropdownOpen: PropTypes.bool, |
||||
onSelect: PropTypes.func, |
||||
openDropdown: PropTypes.func, |
||||
selectedAccount: PropTypes.object, |
||||
} |
||||
|
||||
static contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
render () { |
||||
const { |
||||
accounts, |
||||
closeDropdown, |
||||
dropdownOpen, |
||||
openDropdown, |
||||
selectedAccount, |
||||
onSelect, |
||||
} = this.props |
||||
|
||||
return <div className="send-v2__from-dropdown"> |
||||
<AccountListItem |
||||
account={selectedAccount} |
||||
handleClick={openDropdown} |
||||
icon={<i className={`fa fa-caret-down fa-lg`} style={ { color: '#dedede' } }/>} |
||||
/> |
||||
{dropdownOpen && <SendDropdownList |
||||
accounts={accounts} |
||||
closeDropdown={closeDropdown} |
||||
onSelect={onSelect} |
||||
activeAddress={selectedAccount.address} |
||||
/>} |
||||
</div> |
||||
} |
||||
|
||||
} |
@ -1 +0,0 @@ |
||||
export { default } from './from-dropdown.component' |
@ -1,88 +0,0 @@ |
||||
import React from 'react' |
||||
import assert from 'assert' |
||||
import { shallow } from 'enzyme' |
||||
import sinon from 'sinon' |
||||
import FromDropdown from '../from-dropdown.component.js' |
||||
|
||||
import AccountListItem from '../../../../account-list-item/account-list-item.container' |
||||
import SendDropdownList from '../../../send-dropdown-list/send-dropdown-list.component' |
||||
|
||||
const propsMethodSpies = { |
||||
closeDropdown: sinon.spy(), |
||||
openDropdown: sinon.spy(), |
||||
onSelect: sinon.spy(), |
||||
} |
||||
|
||||
describe('FromDropdown Component', function () { |
||||
let wrapper |
||||
|
||||
beforeEach(() => { |
||||
wrapper = shallow(<FromDropdown |
||||
accounts={['mockAccount']} |
||||
closeDropdown={propsMethodSpies.closeDropdown} |
||||
dropdownOpen={false} |
||||
onSelect={propsMethodSpies.onSelect} |
||||
openDropdown={propsMethodSpies.openDropdown} |
||||
selectedAccount={ { address: 'mockAddress' } } |
||||
/>, { context: { t: str => str + '_t' } }) |
||||
}) |
||||
|
||||
afterEach(() => { |
||||
propsMethodSpies.closeDropdown.resetHistory() |
||||
propsMethodSpies.openDropdown.resetHistory() |
||||
propsMethodSpies.onSelect.resetHistory() |
||||
}) |
||||
|
||||
describe('render', () => { |
||||
it('should render a div with a .send-v2__from-dropdown class', () => { |
||||
assert.equal(wrapper.find('.send-v2__from-dropdown').length, 1) |
||||
}) |
||||
|
||||
it('should render an AccountListItem as the first child of the .send-v2__from-dropdown div', () => { |
||||
assert(wrapper.find('.send-v2__from-dropdown').childAt(0).is(AccountListItem)) |
||||
}) |
||||
|
||||
it('should pass the correct props to AccountListItem', () => { |
||||
const { |
||||
account, |
||||
handleClick, |
||||
icon, |
||||
} = wrapper.find('.send-v2__from-dropdown').childAt(0).props() |
||||
assert.deepEqual(account, { address: 'mockAddress' }) |
||||
assert.deepEqual( |
||||
icon, |
||||
<i className={`fa fa-caret-down fa-lg`} style={ { color: '#dedede' } }/> |
||||
) |
||||
assert.equal(propsMethodSpies.openDropdown.callCount, 0) |
||||
handleClick() |
||||
assert.equal(propsMethodSpies.openDropdown.callCount, 1) |
||||
}) |
||||
|
||||
it('should not render a SendDropdownList when dropdownOpen is false', () => { |
||||
assert.equal(wrapper.find(SendDropdownList).length, 0) |
||||
}) |
||||
|
||||
it('should render a SendDropdownList when dropdownOpen is true', () => { |
||||
wrapper.setProps({ dropdownOpen: true }) |
||||
assert(wrapper.find(SendDropdownList).length, 1) |
||||
}) |
||||
|
||||
it('should pass the correct props to the SendDropdownList]', () => { |
||||
wrapper.setProps({ dropdownOpen: true }) |
||||
const { |
||||
accounts, |
||||
closeDropdown, |
||||
onSelect, |
||||
activeAddress, |
||||
} = wrapper.find(SendDropdownList).props() |
||||
assert.deepEqual(accounts, ['mockAccount']) |
||||
assert.equal(activeAddress, 'mockAddress') |
||||
assert.equal(propsMethodSpies.closeDropdown.callCount, 0) |
||||
closeDropdown() |
||||
assert.equal(propsMethodSpies.closeDropdown.callCount, 1) |
||||
assert.equal(propsMethodSpies.onSelect.callCount, 0) |
||||
onSelect() |
||||
assert.equal(propsMethodSpies.onSelect.callCount, 1) |
||||
}) |
||||
}) |
||||
}) |
@ -1,64 +1,27 @@ |
||||
import React, { Component } from 'react' |
||||
import PropTypes from 'prop-types' |
||||
import SendRowWrapper from '../send-row-wrapper/' |
||||
import FromDropdown from './from-dropdown/' |
||||
import AccountListItem from '../../account-list-item' |
||||
|
||||
export default class SendFromRow extends Component { |
||||
|
||||
static propTypes = { |
||||
closeFromDropdown: PropTypes.func, |
||||
conversionRate: PropTypes.number, |
||||
from: PropTypes.object, |
||||
fromAccounts: PropTypes.array, |
||||
fromDropdownOpen: PropTypes.bool, |
||||
openFromDropdown: PropTypes.func, |
||||
tokenContract: PropTypes.object, |
||||
updateSendFrom: PropTypes.func, |
||||
setSendTokenBalance: PropTypes.func, |
||||
} |
||||
|
||||
static contextTypes = { |
||||
t: PropTypes.func, |
||||
} |
||||
|
||||
async handleFromChange (newFrom) { |
||||
const { |
||||
updateSendFrom, |
||||
tokenContract, |
||||
setSendTokenBalance, |
||||
} = this.props |
||||
|
||||
if (tokenContract) { |
||||
const usersToken = await tokenContract.balanceOf(newFrom.address) |
||||
setSendTokenBalance(usersToken) |
||||
} |
||||
|
||||
updateSendFrom(newFrom) |
||||
} |
||||
|
||||
render () { |
||||
const { |
||||
closeFromDropdown, |
||||
conversionRate, |
||||
from, |
||||
fromAccounts, |
||||
fromDropdownOpen, |
||||
openFromDropdown, |
||||
} = this.props |
||||
const { t } = this.context |
||||
const { from } = this.props |
||||
|
||||
return ( |
||||
<SendRowWrapper label={`${this.context.t('from')}:`}> |
||||
<FromDropdown |
||||
accounts={fromAccounts} |
||||
closeDropdown={() => closeFromDropdown()} |
||||
conversionRate={conversionRate} |
||||
dropdownOpen={fromDropdownOpen} |
||||
onSelect={newFrom => this.handleFromChange(newFrom)} |
||||
openDropdown={() => openFromDropdown()} |
||||
selectedAccount={from} |
||||
/> |
||||
<SendRowWrapper label={`${t('from')}:`}> |
||||
<div className="send-v2__from-dropdown"> |
||||
<AccountListItem account={from} /> |
||||
</div> |
||||
</SendRowWrapper> |
||||
) |
||||
} |
||||
|
||||
} |
||||
|
@ -1,46 +1,11 @@ |
||||
import { connect } from 'react-redux' |
||||
import { |
||||
accountsWithSendEtherInfoSelector, |
||||
getConversionRate, |
||||
getSelectedTokenContract, |
||||
getSendFromObject, |
||||
} from '../../send.selectors.js' |
||||
import { |
||||
getFromDropdownOpen, |
||||
} from './send-from-row.selectors.js' |
||||
import { calcTokenBalance } from '../../send.utils.js' |
||||
import { |
||||
updateSendFrom, |
||||
setSendTokenBalance, |
||||
} from '../../../../actions' |
||||
import { |
||||
closeFromDropdown, |
||||
openFromDropdown, |
||||
} from '../../../../ducks/send.duck' |
||||
import { getSendFromObject } from '../../send.selectors.js' |
||||
import SendFromRow from './send-from-row.component' |
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SendFromRow) |
||||
|
||||
function mapStateToProps (state) { |
||||
return { |
||||
conversionRate: getConversionRate(state), |
||||
from: getSendFromObject(state), |
||||
fromAccounts: accountsWithSendEtherInfoSelector(state), |
||||
fromDropdownOpen: getFromDropdownOpen(state), |
||||
tokenContract: getSelectedTokenContract(state), |
||||
} |
||||
} |
||||
|
||||
function mapDispatchToProps (dispatch) { |
||||
return { |
||||
closeFromDropdown: () => dispatch(closeFromDropdown()), |
||||
openFromDropdown: () => dispatch(openFromDropdown()), |
||||
updateSendFrom: newFrom => dispatch(updateSendFrom(newFrom)), |
||||
setSendTokenBalance: (usersToken, selectedToken) => { |
||||
if (!usersToken) return |
||||
|
||||
const tokenBalance = calcTokenBalance({ usersToken, selectedToken }) |
||||
dispatch(setSendTokenBalance(tokenBalance)) |
||||
}, |
||||
} |
||||
} |
||||
export default connect(mapStateToProps)(SendFromRow) |
||||
|
@ -1,121 +1,31 @@ |
||||
import React from 'react' |
||||
import assert from 'assert' |
||||
import { shallow } from 'enzyme' |
||||
import sinon from 'sinon' |
||||
import SendFromRow from '../send-from-row.component.js' |
||||
|
||||
import AccountListItem from '../../../account-list-item' |
||||
import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component' |
||||
import FromDropdown from '../from-dropdown/from-dropdown.component' |
||||
|
||||
const propsMethodSpies = { |
||||
closeFromDropdown: sinon.spy(), |
||||
openFromDropdown: sinon.spy(), |
||||
updateSendFrom: sinon.spy(), |
||||
setSendTokenBalance: sinon.spy(), |
||||
} |
||||
|
||||
sinon.spy(SendFromRow.prototype, 'handleFromChange') |
||||
|
||||
describe('SendFromRow Component', function () { |
||||
let wrapper |
||||
let instance |
||||
|
||||
beforeEach(() => { |
||||
wrapper = shallow(<SendFromRow |
||||
closeFromDropdown={propsMethodSpies.closeFromDropdown} |
||||
conversionRate={15} |
||||
from={ { address: 'mockAddress' } } |
||||
fromAccounts={['mockAccount']} |
||||
fromDropdownOpen={false} |
||||
openFromDropdown={propsMethodSpies.openFromDropdown} |
||||
setSendTokenBalance={propsMethodSpies.setSendTokenBalance} |
||||
tokenContract={null} |
||||
updateSendFrom={propsMethodSpies.updateSendFrom} |
||||
/>, { context: { t: str => str + '_t' } }) |
||||
instance = wrapper.instance() |
||||
}) |
||||
|
||||
afterEach(() => { |
||||
propsMethodSpies.closeFromDropdown.resetHistory() |
||||
propsMethodSpies.openFromDropdown.resetHistory() |
||||
propsMethodSpies.updateSendFrom.resetHistory() |
||||
propsMethodSpies.setSendTokenBalance.resetHistory() |
||||
SendFromRow.prototype.handleFromChange.resetHistory() |
||||
}) |
||||
|
||||
describe('handleFromChange', () => { |
||||
|
||||
it('should call updateSendFrom', () => { |
||||
assert.equal(propsMethodSpies.updateSendFrom.callCount, 0) |
||||
instance.handleFromChange('mockFrom') |
||||
assert.equal(propsMethodSpies.updateSendFrom.callCount, 1) |
||||
assert.deepEqual( |
||||
propsMethodSpies.updateSendFrom.getCall(0).args, |
||||
['mockFrom'] |
||||
) |
||||
}) |
||||
|
||||
it('should call tokenContract.balanceOf and setSendTokenBalance if tokenContract is defined', async () => { |
||||
wrapper.setProps({ |
||||
tokenContract: { |
||||
balanceOf: () => new Promise((resolve) => resolve('mockUsersToken')), |
||||
}, |
||||
}) |
||||
assert.equal(propsMethodSpies.setSendTokenBalance.callCount, 0) |
||||
await instance.handleFromChange('mockFrom') |
||||
assert.equal(propsMethodSpies.setSendTokenBalance.callCount, 1) |
||||
assert.deepEqual( |
||||
propsMethodSpies.setSendTokenBalance.getCall(0).args, |
||||
['mockUsersToken'] |
||||
) |
||||
}) |
||||
|
||||
}) |
||||
|
||||
describe('render', () => { |
||||
const wrapper = shallow( |
||||
<SendFromRow |
||||
from={ { address: 'mockAddress' } } |
||||
/>, |
||||
{ context: { t: str => str + '_t' } } |
||||
) |
||||
|
||||
it('should render a SendRowWrapper component', () => { |
||||
assert.equal(wrapper.find(SendRowWrapper).length, 1) |
||||
}) |
||||
|
||||
it('should pass the correct props to SendRowWrapper', () => { |
||||
const { |
||||
label, |
||||
} = wrapper.find(SendRowWrapper).props() |
||||
|
||||
const { label } = wrapper.find(SendRowWrapper).props() |
||||
assert.equal(label, 'from_t:') |
||||
}) |
||||
|
||||
it('should render an FromDropdown as a child of the SendRowWrapper', () => { |
||||
assert(wrapper.find(SendRowWrapper).childAt(0).is(FromDropdown)) |
||||
}) |
||||
|
||||
it('should render the FromDropdown with the correct props', () => { |
||||
const { |
||||
accounts, |
||||
closeDropdown, |
||||
conversionRate, |
||||
dropdownOpen, |
||||
onSelect, |
||||
openDropdown, |
||||
selectedAccount, |
||||
} = wrapper.find(SendRowWrapper).childAt(0).props() |
||||
assert.deepEqual(accounts, ['mockAccount']) |
||||
assert.equal(dropdownOpen, false) |
||||
assert.equal(conversionRate, 15) |
||||
assert.deepEqual(selectedAccount, { address: 'mockAddress' }) |
||||
assert.equal(propsMethodSpies.closeFromDropdown.callCount, 0) |
||||
closeDropdown() |
||||
assert.equal(propsMethodSpies.closeFromDropdown.callCount, 1) |
||||
assert.equal(propsMethodSpies.openFromDropdown.callCount, 0) |
||||
openDropdown() |
||||
assert.equal(propsMethodSpies.openFromDropdown.callCount, 1) |
||||
assert.equal(SendFromRow.prototype.handleFromChange.callCount, 0) |
||||
onSelect('mockNewFrom') |
||||
assert.equal(SendFromRow.prototype.handleFromChange.callCount, 1) |
||||
assert.deepEqual( |
||||
SendFromRow.prototype.handleFromChange.getCall(0).args, |
||||
['mockNewFrom'] |
||||
) |
||||
const { account } = wrapper.find(AccountListItem).props() |
||||
assert.deepEqual(account, { address: 'mockAddress' }) |
||||
}) |
||||
}) |
||||
}) |
||||
|
@ -1,110 +1,26 @@ |
||||
import assert from 'assert' |
||||
import proxyquire from 'proxyquire' |
||||
import sinon from 'sinon' |
||||
|
||||
let mapStateToProps |
||||
let mapDispatchToProps |
||||
|
||||
const actionSpies = { |
||||
updateSendFrom: sinon.spy(), |
||||
setSendTokenBalance: sinon.spy(), |
||||
} |
||||
const duckActionSpies = { |
||||
closeFromDropdown: sinon.spy(), |
||||
openFromDropdown: sinon.spy(), |
||||
} |
||||
|
||||
proxyquire('../send-from-row.container.js', { |
||||
'react-redux': { |
||||
connect: (ms, md) => { |
||||
connect: ms => { |
||||
mapStateToProps = ms |
||||
mapDispatchToProps = md |
||||
return () => ({}) |
||||
}, |
||||
}, |
||||
'../../send.selectors.js': { |
||||
accountsWithSendEtherInfoSelector: (s) => `mockFromAccounts:${s}`, |
||||
getConversionRate: (s) => `mockConversionRate:${s}`, |
||||
getSelectedTokenContract: (s) => `mockTokenContract:${s}`, |
||||
getSendFromObject: (s) => `mockFrom:${s}`, |
||||
}, |
||||
'./send-from-row.selectors.js': { getFromDropdownOpen: (s) => `mockFromDropdownOpen:${s}` }, |
||||
'../../send.utils.js': { calcTokenBalance: ({ usersToken, selectedToken }) => usersToken + selectedToken }, |
||||
'../../../../actions': actionSpies, |
||||
'../../../../ducks/send.duck': duckActionSpies, |
||||
}) |
||||
|
||||
describe('send-from-row container', () => { |
||||
|
||||
describe('mapStateToProps()', () => { |
||||
|
||||
it('should map the correct properties to props', () => { |
||||
assert.deepEqual(mapStateToProps('mockState'), { |
||||
conversionRate: 'mockConversionRate:mockState', |
||||
from: 'mockFrom:mockState', |
||||
fromAccounts: 'mockFromAccounts:mockState', |
||||
fromDropdownOpen: 'mockFromDropdownOpen:mockState', |
||||
tokenContract: 'mockTokenContract:mockState', |
||||
}) |
||||
}) |
||||
|
||||
}) |
||||
|
||||
describe('mapDispatchToProps()', () => { |
||||
let dispatchSpy |
||||
let mapDispatchToPropsObject |
||||
|
||||
beforeEach(() => { |
||||
dispatchSpy = sinon.spy() |
||||
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy) |
||||
}) |
||||
|
||||
describe('closeFromDropdown()', () => { |
||||
it('should dispatch a closeFromDropdown action', () => { |
||||
mapDispatchToPropsObject.closeFromDropdown() |
||||
assert(dispatchSpy.calledOnce) |
||||
assert(duckActionSpies.closeFromDropdown.calledOnce) |
||||
assert.equal( |
||||
duckActionSpies.closeFromDropdown.getCall(0).args[0], |
||||
undefined |
||||
) |
||||
}) |
||||
}) |
||||
|
||||
describe('openFromDropdown()', () => { |
||||
it('should dispatch a openFromDropdown action', () => { |
||||
mapDispatchToPropsObject.openFromDropdown() |
||||
assert(dispatchSpy.calledOnce) |
||||
assert(duckActionSpies.openFromDropdown.calledOnce) |
||||
assert.equal( |
||||
duckActionSpies.openFromDropdown.getCall(0).args[0], |
||||
undefined |
||||
) |
||||
}) |
||||
}) |
||||
|
||||
describe('updateSendFrom()', () => { |
||||
it('should dispatch an updateSendFrom action', () => { |
||||
mapDispatchToPropsObject.updateSendFrom('mockFrom') |
||||
assert(dispatchSpy.calledOnce) |
||||
assert.equal( |
||||
actionSpies.updateSendFrom.getCall(0).args[0], |
||||
'mockFrom' |
||||
) |
||||
}) |
||||
}) |
||||
|
||||
describe('setSendTokenBalance()', () => { |
||||
it('should dispatch an setSendTokenBalance action', () => { |
||||
mapDispatchToPropsObject.setSendTokenBalance('mockUsersToken', 'mockSelectedToken') |
||||
assert(dispatchSpy.calledOnce) |
||||
assert.equal( |
||||
actionSpies.setSendTokenBalance.getCall(0).args[0], |
||||
'mockUsersTokenmockSelectedToken' |
||||
) |
||||
}) |
||||
}) |
||||
|
||||
}) |
||||
|
||||
}) |
||||
|
Loading…
Reference in new issue