Browse Source

feat(wallet): implement new pay forms

renovate/lint-staged-8.x
Tom Kirkpatrick 6 years ago
parent
commit
2b80ae7106
No known key found for this signature in database GPG Key ID: 72203A8EC5967EA8
  1. 44
      app/components/Form/Form.js
  2. 115
      app/components/Pay/Pay.js
  3. 3
      app/components/UI/Input.js
  4. 4
      app/components/UI/Range.js
  5. 3
      app/components/UI/TextArea.js
  6. 8
      app/components/Value/Value.js
  7. 41
      app/containers/Pay.js
  8. 4
      app/lib/lnd/methods/index.js
  9. 8
      app/lib/utils/api.js
  10. 4
      app/main.dev.js
  11. 2
      app/reducers/index.js
  12. 13
      app/reducers/ipc.js
  13. 131
      app/reducers/pay.js
  14. 1
      internals/webpack/webpack.config.renderer.dev.js
  15. 7
      internals/webpack/webpack.config.renderer.prod.js
  16. 2
      stories/components/form.stories.js
  17. 7
      stories/pages/pay.stories.js
  18. 2
      test/unit/components/Form.spec.js
  19. 87
      test/unit/components/Form/Pay.spec.js
  20. 1
      test/unit/components/Pay/PaySummaryOnchain.spec.js
  21. 2
      test/unit/components/UI/__snapshots__/Range.spec.js.snap

44
app/components/Form/Form.js

@ -1,34 +1,38 @@
import React from 'react'
import PropTypes from 'prop-types'
import X from 'components/Icon/X'
import Pay from './Pay'
import { Modal } from 'components/UI'
import Pay from 'containers/Pay'
import Request from './Request'
import styles from './Form.scss'
const FORM_TYPES = {
PAY_FORM: Pay,
REQUEST_FORM: Request
}
const Form = ({ formType, formProps, closeForm }) => {
if (!formType) {
return null
}
const FormComponent = FORM_TYPES[formType]
return (
<div className={styles.container}>
<div className={styles.closeContainer}>
<span onClick={closeForm}>
<X />
</span>
</div>
<FormComponent {...formProps} />
</div>
)
switch (formType) {
case 'PAY_FORM':
return (
<div className={styles.container}>
<Modal onClose={closeForm}>
<Pay width={9 / 16} mx="auto" />
</Modal>
</div>
)
case 'REQUEST_FORM':
return (
<div className={styles.container}>
<div className={styles.closeContainer}>
<span onClick={closeForm}>
<X />
</span>
</div>
<Request {...formProps} />
</div>
)
}
}
Form.propTypes = {

115
app/components/Pay/Pay.js

@ -124,6 +124,7 @@ class Pay extends React.Component {
}
static defaultProps = {
initialPayReq: null,
initialAmountCrypto: null,
initialAmountFiat: null,
isProcessing: false,
@ -135,6 +136,7 @@ class Pay extends React.Component {
state = {
currentStep: 'address',
previousStep: null,
isLn: null,
isOnchain: null
}
@ -142,21 +144,18 @@ class Pay extends React.Component {
amountInput = React.createRef()
payReqInput = React.createRef()
/**
* If we have an address when the component mounts, run the payReq change handler to compure isLn / isOnchain.
*/
componentDidMount() {
const { formApi } = this
if (formApi.getValue('payReq')) {
this.handlePayReqOnChange()
componentDidUpdate(prevProps, prevState) {
const { initialPayReq, queryRoutes } = this.props
const { currentStep, invoice, isLn, isOnchain } = this.state
// If initialPayReq has been set, reset the form and submit as new
if (initialPayReq && initialPayReq !== prevProps.initialPayReq) {
this.formApi.reset()
this.formApi.setValue('payReq', initialPayReq)
this.handlePayReqChange()
}
}
/**
* If we have gone back to the address step, focus the address input and unmark all fields from being touched.
*/
componentDidUpdate(prevProps, prevState) {
const { currentStep } = this.state
// If we have gone back to the address step, unmark all fields from being touched.
if (currentStep !== prevState.currentStep) {
if (currentStep === 'address') {
Object.keys(this.formApi.getState().touched).forEach(field => {
@ -164,6 +163,21 @@ class Pay extends React.Component {
})
}
}
// If we now have a valid onchain address, trigger the form submit.
if (isOnchain && isOnchain !== prevState.isOnchain) {
this.formApi.submitForm()
}
// If we now have a valid offchain address, trigger the form submit.
if (isLn && isLn !== prevState.isLn) {
this.formApi.submitForm()
// And if now have a valid lightning invoice, call queryRoutes.
if (invoice) {
const { satoshis, payeeNodeKey } = invoice
queryRoutes(payeeNodeKey, satoshis)
}
}
}
/**
@ -193,15 +207,6 @@ class Pay extends React.Component {
this.formApi = formApi
}
/**
* set the amountFiat field.
*/
setAmountFiat = () => {
if (this.amountInput.current) {
this.amountInput.current.focus()
}
}
/**
* Focus the payReq input.
*/
@ -241,7 +246,7 @@ class Pay extends React.Component {
const { currentStep } = this.state
const nextStep = Math.max(this.steps().indexOf(currentStep) - 1, 0)
if (currentStep !== nextStep) {
this.setState({ currentStep: this.steps()[nextStep] })
this.setState({ currentStep: this.steps()[nextStep], previousStep: currentStep })
}
}
@ -252,15 +257,15 @@ class Pay extends React.Component {
const { currentStep } = this.state
const nextStep = Math.min(this.steps().indexOf(currentStep) + 1, this.steps().length - 1)
if (currentStep !== nextStep) {
this.setState({ currentStep: this.steps()[nextStep] })
this.setState({ currentStep: this.steps()[nextStep], previousStep: currentStep })
}
}
/**
* Set isLn/isOnchain state based on payReq value.
*/
handlePayReqOnChange = () => {
const { chain, network, queryRoutes } = this.props
handlePayReqChange = () => {
const { chain, network } = this.props
const payReq = this.formApi.getValue('payReq')
const state = {
isLn: null,
@ -278,8 +283,6 @@ class Pay extends React.Component {
return
}
state.isLn = true
const { satoshis, payeeNodeKey } = invoice
queryRoutes(payeeNodeKey, satoshis)
}
// Otherwise, see if we have a valid onchain address.
@ -289,10 +292,19 @@ class Pay extends React.Component {
// Update the state with our findings.
this.setState(state)
}
// As soon as we have detected a valid address, submit the form.
if (state.isLn || state.isOnchain) {
this.formApi.submitForm()
/**
* Handle the case when the form is mountedwith an initialPayReq.
* This is the earliest possibleplace we can do this because the form is not initialised in ComponentDidMount.
*/
handleChange = formState => {
const { initialPayReq } = this.props
const { currentStep, previousStep } = this.state
// If this is the first time the address page is showing and we have an initialPayReq, process the request
// as if the user had entered it themselves.
if (currentStep === 'address' && !previousStep && initialPayReq && formState.values.payReq) {
this.handlePayReqChange()
}
}
@ -333,7 +345,14 @@ class Pay extends React.Component {
}
renderHelpText = () => {
const { currentStep } = this.state
const { initialPayReq } = this.props
const { currentStep, previousStep } = this.state
// Do not render the help text if the form has just loadad with an initial payment request.
if (initialPayReq && !previousStep) {
return null
}
return (
<Transition
native
@ -380,15 +399,16 @@ class Pay extends React.Component {
{styles => (
<React.Fragment>
<LightningInvoiceInput
field="payReq"
name="payReq"
style={styles}
initialValue={initialPayReq}
required
chain={chain}
network={network}
field="payReq"
validateOnBlur
validateOnChange
onChange={this.handlePayReqOnChange}
onChange={this.handlePayReqChange}
width={1}
readOnly={currentStep !== 'address'}
forwardedRef={this.payReqInput}
@ -405,7 +425,7 @@ class Pay extends React.Component {
}
renderAmountFields = () => {
const { currentStep } = this.state
const { currentStep, isOnchain } = this.state
const {
cryptoCurrency,
cryptoCurrencies,
@ -415,6 +435,12 @@ class Pay extends React.Component {
initialAmountCrypto,
initialAmountFiat
} = this.props
// Do not render unless we are working with an onchain address.
if (!isOnchain) {
return null
}
return (
<ShowHideAmount
state={currentStep === 'amount' ? 'show' : currentStep === 'address' ? 'hide' : 'remove'}
@ -431,10 +457,11 @@ class Pay extends React.Component {
<Flex width={6 / 13}>
<Box width={145}>
<CryptoAmountInput
field="amountCrypto"
name="amountCrypto"
initialValue={initialAmountCrypto}
currency={cryptoCurrency}
required
field="amountCrypto"
width={145}
validateOnChange
validateOnBlur
@ -457,10 +484,11 @@ class Pay extends React.Component {
<Flex width={6 / 13}>
<Box width={145} ml="auto">
<FiatAmountInput
field="amountFiat"
name="amountFiat"
initialValue={initialAmountFiat}
currency={fiatCurrency}
currentTicker={currentTicker}
field="amountFiat"
width={145}
onChange={this.handleAmountFiatChange}
disabled={currentStep === 'address'}
@ -483,7 +511,7 @@ class Pay extends React.Component {
}
renderSummary = () => {
const { isOnchain } = this.state
const { currentStep, isOnchain } = this.state
const {
cryptoCurrency,
cryptoCurrencyTicker,
@ -498,6 +526,12 @@ class Pay extends React.Component {
routes,
setCryptoCurrency
} = this.props
// Do not render unless we are on the summary step.
if (currentStep !== 'summary') {
return null
}
const formState = this.formApi.getState()
let minFee, maxFee
if (routes.length) {
@ -582,6 +616,7 @@ class Pay extends React.Component {
css={{ height: '100%' }}
{...rest}
getApi={this.setFormApi}
onChange={this.handleChange}
onSubmit={this.onSubmit}
>
{({ formState }) => {
@ -634,8 +669,8 @@ class Pay extends React.Component {
<Box as="section" css={{ flex: 1 }} mb={3}>
{this.renderHelpText()}
{this.renderAddressField()}
{isOnchain && this.renderAmountFields()}
{currentStep === 'summary' && this.renderSummary()}
{this.renderAmountFields()}
{this.renderSummary()}
</Box>
<Box as="footer" mt="auto">

3
app/components/UI/Input.js

@ -49,7 +49,6 @@ class Input extends React.Component {
onChange,
onBlur,
onFocus,
initialValue,
forwardedRef,
theme,
fieldApi,
@ -90,7 +89,7 @@ class Input extends React.Component {
)}
{...rest}
ref={this.inputRef}
value={!value && value !== 0 ? '' : initialValue || value}
value={!value && value !== 0 ? '' : value}
onChange={e => {
setValue(e.target.value)
if (onChange) {

4
app/components/UI/Range.js

@ -28,7 +28,7 @@ const Input = styled.input`
const Range = asField(({ fieldState, fieldApi, ...props }) => {
const { value } = fieldState
const { setValue, setTouched } = fieldApi
const { onChange, onBlur, initialValue, forwardedRef, ...rest } = props
const { onChange, onBlur, forwardedRef, ...rest } = props
return (
<Input
min={0}
@ -37,7 +37,7 @@ const Range = asField(({ fieldState, fieldApi, ...props }) => {
{...rest}
type="range"
ref={forwardedRef}
value={value || initialValue || '0'}
value={value || 0}
onChange={e => {
setValue(e.target.value)
if (onChange) {

3
app/components/UI/TextArea.js

@ -50,7 +50,6 @@ class TextArea extends React.PureComponent {
onChange,
onBlur,
onFocus,
initialValue,
forwardedRef,
theme,
fieldApi,
@ -92,7 +91,7 @@ class TextArea extends React.PureComponent {
)}
{...rest}
ref={this.inputRef}
value={!value && value !== 0 ? '' : initialValue || value}
value={!value && value !== 0 ? '' : value}
onChange={e => {
setValue(e.target.value)
if (onChange) {

8
app/components/Value/Value.js

@ -10,7 +10,13 @@ const Value = ({ value, currency, currentTicker, fiatTicker }) => {
if (currency === 'fiat') {
price = currentTicker[fiatTicker].last
}
return <i>{Number(convert('sats', currency, value, price))}</i>
return (
<i>
{Number(convert('sats', currency, value, price))
.toFixed(8)
.replace(/\.?0+$/, '')}
</i>
)
}
Value.propTypes = {

41
app/containers/Pay.js

@ -0,0 +1,41 @@
import { connect } from 'react-redux'
import { Pay } from 'components/Pay'
import { tickerSelectors, setCurrency, setFiatTicker } from 'reducers/ticker'
import { queryFees, queryRoutes } from 'reducers/pay'
import { infoSelectors } from 'reducers/info'
import { sendCoins } from 'reducers/transaction'
import { payInvoice } from 'reducers/payment'
const mapStateToProps = state => ({
chain: state.info.chain,
network: infoSelectors.testnetSelector(state) ? 'testnet' : 'mainnet',
cryptoName: tickerSelectors.cryptoName(state),
channelBalance: state.balance.channelBalance,
currentTicker: tickerSelectors.currentTicker(state),
cryptoCurrency: state.ticker.currency,
cryptoCurrencyTicker: tickerSelectors.currencyName(state),
cryptoCurrencies: state.ticker.currencyFilters,
fiatCurrencies: state.ticker.fiatTickers,
fiatCurrency: state.ticker.fiatTicker,
initialPayReq: state.pay.payReq,
isQueryingFees: state.pay.isQueryingFees,
isQueryingRoutes: state.pay.isQueryingRoutes,
nodes: state.network.nodes,
onchainFees: state.pay.onchainFees,
routes: state.pay.routes,
walletBalance: state.balance.walletBalance
})
const mapDispatchToProps = {
payInvoice,
setCryptoCurrency: setCurrency,
setFiatCurrency: setFiatTicker,
sendCoins,
queryFees,
queryRoutes
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Pay)

4
app/lib/lnd/methods/index.js

@ -40,10 +40,10 @@ export default function(lnd, log, event, msg, data) {
// Data looks like { pubkey: String, amount: Number }
networkController
.queryRoutes(lnd, data)
.then(routes => event.sender.send('receiveQueryRoutes', routes))
.then(routes => event.sender.send('queryRoutesSuccess', routes))
.catch(error => {
log.error('queryRoutes:', error)
event.sender.send('queryRoutesFailed', { error: error.toString() })
event.sender.send('queryRoutesFailure', { error: error.toString() })
})
break
case 'getInvoiceAndQueryRoutes':

8
app/lib/utils/api.js

@ -35,3 +35,11 @@ export function requestSuggestedNodes() {
url: BASE_URL
}).then(response => response.data)
}
export function requestFees() {
const BASE_URL = 'https://bitcoinfees.earn.com/api/v1/fees/recommended'
return axios({
method: 'get',
url: BASE_URL
}).then(response => response.data)
}

4
app/main.dev.js

@ -166,8 +166,8 @@ app.on('ready', async () => {
app.on('open-url', (event, url) => {
mainLog.debug('open-url')
event.preventDefault()
const payreq = url.split(':')[1]
zap.sendMessage('lightningPaymentUri', { payreq })
const payReq = url.split(':')[1]
zap.sendMessage('lightningPaymentUri', { payReq })
zap.mainWindow.show()
})

2
app/reducers/index.js

@ -13,6 +13,7 @@ import peers from './peers'
import channels from './channels'
import contactsform from './contactsform'
import form from './form'
import pay from './pay'
import payform from './payform'
import requestform from './requestform'
import invoice from './invoice'
@ -42,6 +43,7 @@ const rootReducer = combineReducers({
channels,
contactsform,
form,
pay,
payform,
requestform,
invoice,

13
app/reducers/ipc.js

@ -27,7 +27,7 @@ import {
channelGraphData,
channelGraphStatus
} from './channels'
import { lightningPaymentUri } from './payform'
import { lightningPaymentUri, queryRoutesSuccess, queryRoutesFailure } from './pay'
import { receivePayments, paymentSuccessful, paymentFailed } from './payment'
import {
receiveInvoices,
@ -44,12 +44,7 @@ import {
newTransaction
} from './transaction'
import {
receiveDescribeNetwork,
receiveQueryRoutes,
receiveInvoiceAndQueryRoutes,
queryRoutesFailed
} from './network'
import { receiveDescribeNetwork, receiveQueryRoutes, receiveInvoiceAndQueryRoutes } from './network'
import {
startOnboarding,
@ -89,6 +84,9 @@ const ipc = createIpc({
lightningPaymentUri,
queryRoutesSuccess,
queryRoutesFailure,
paymentSuccessful,
paymentFailed,
@ -120,7 +118,6 @@ const ipc = createIpc({
receiveDescribeNetwork,
receiveQueryRoutes,
receiveInvoiceAndQueryRoutes,
queryRoutesFailed,
startOnboarding,
startLndError,

131
app/reducers/pay.js

@ -0,0 +1,131 @@
import { ipcRenderer } from 'electron'
import get from 'lodash.get'
import { requestFees } from 'lib/utils/api'
import { setFormType } from './form'
// ------------------------------------
// Constants
// ------------------------------------
export const QUERY_FEES = 'QUERY_FEES'
export const QUERY_FEES_SUCCESS = 'QUERY_FEES_SUCCESS'
export const QUERY_FEES_FAILURE = 'QUERY_FEES_FAILURE'
export const QUERY_ROUTES = 'QUERY_ROUTES'
export const QUERY_ROUTES_SUCCESS = 'QUERY_ROUTES_SUCCESS'
export const QUERY_ROUTES_FAILURE = 'QUERY_ROUTES_FAILURE'
export const SET_PAY_REQ = 'SET_PAY_REQ'
// ------------------------------------
// Actions
// ------------------------------------
export const queryFees = () => async dispatch => {
dispatch({ type: QUERY_FEES })
try {
const onchainFees = await requestFees()
dispatch({ type: QUERY_FEES_SUCCESS, onchainFees })
} catch (e) {
const error = get(e, 'response.statusText', e)
dispatch({ type: QUERY_FEES_FAILURE, error })
}
}
export const queryRoutes = (pubKey, amount) => dispatch => {
dispatch({ type: QUERY_ROUTES, pubKey })
ipcRenderer.send('lnd', { msg: 'queryRoutes', data: { pubkey: pubKey, amount } })
}
export const queryRoutesSuccess = (event, { routes }) => dispatch =>
dispatch({ type: QUERY_ROUTES_SUCCESS, routes })
export const queryRoutesFailure = () => dispatch => {
dispatch({ type: QUERY_ROUTES_FAILURE })
}
export function setPayReq(payReq) {
return {
type: SET_PAY_REQ,
payReq
}
}
export const lightningPaymentUri = (event, { payReq }) => dispatch => {
dispatch(setPayReq(payReq))
dispatch(setFormType('PAY_FORM'))
dispatch(setPayReq(null))
}
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
[QUERY_FEES]: state => ({
...state,
isQueryingFees: true,
onchainFees: {},
queryFeesError: null
}),
[QUERY_FEES_SUCCESS]: (state, { onchainFees }) => ({
...state,
isQueryingFees: false,
onchainFees,
queryFeesError: null
}),
[QUERY_FEES_FAILURE]: (state, { error }) => ({
...state,
isQueryingFees: false,
onchainFees: {},
queryFeesError: error
}),
[QUERY_ROUTES]: (state, { pubKey }) => ({
...state,
isQueryingRoutes: true,
pubKey,
queryRoutesError: null,
routes: []
}),
[QUERY_ROUTES_SUCCESS]: (state, { routes }) => ({
...state,
isQueryingRoutes: false,
queryRoutesError: null,
routes
}),
[QUERY_ROUTES_FAILURE]: (state, { error }) => ({
...state,
isQueryingRoutes: false,
pubKey: null,
queryRoutesError: error,
routes: []
}),
[SET_PAY_REQ]: (state, { payReq }) => ({
...state,
payReq
})
}
// ------------------------------------
// Initial State
// ------------------------------------
const initialState = {
isQueryingRoutes: false,
isQueryingFees: false,
onchainFees: {
fastestFee: null,
halfHourFee: null,
hourFee: null
},
payReq: null,
pubKey: null,
queryFeesError: null,
queryRoutesError: null,
routes: []
}
// ------------------------------------
// Reducer
// ------------------------------------
export default function activityReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}

1
internals/webpack/webpack.config.renderer.dev.js

@ -155,6 +155,7 @@ export default merge.smart(baseConfig, {
'http://localhost:*',
'ws://localhost:*',
'https://blockchain.info',
'https://bitcoinfees.earn.com',
'https://zap.jackmallers.com'
],
'script-src': ["'self'", 'http://localhost:*', "'unsafe-eval'"],

7
internals/webpack/webpack.config.renderer.prod.js

@ -98,7 +98,12 @@ export default merge.smart(baseConfig, {
new CspHtmlWebpackPlugin({
'default-src': "'self'",
'object-src': "'none'",
'connect-src': ["'self'", 'https://blockchain.info', 'https://zap.jackmallers.com'],
'connect-src': [
"'self'",
'https://blockchain.info',
'https://bitcoinfees.earn.com',
'https://zap.jackmallers.com'
],
'script-src': ["'self'"],
'font-src': ["'self'", 'data:', 'https://s3.amazonaws.com', 'https://fonts.gstatic.com'],
'style-src': ["'self'", 'blob:', 'https://s3.amazonaws.com', "'unsafe-inline'"]

2
stories/components/form.stories.js

@ -221,7 +221,7 @@ storiesOf('Components.Form', module)
<Label htmlFor="slider1">Example Range</Label>
</Box>
<Box>
<Range field="slider1" onChange={action('change')} />
<Range field="slider1" initialValue={25} onChange={action('change')} />
</Box>
</Box>

7
stories/pages/pay.stories.js

@ -60,18 +60,21 @@ const store = new Store({
})
const mockPayInvoice = async () => {
action('mockPayInvoice')
store.set({ isProcessing: true })
await delay(2000)
store.set({ isProcessing: false })
}
const mockSendCoins = async () => {
action('mockSendCoins')
store.set({ isProcessing: true })
await delay(2000)
store.set({ isProcessing: false })
}
const mockQueryFees = async () => {
action('mockQueryFees')
store.set({ isQueryingFees: true })
await delay(2000)
store.set({
@ -85,6 +88,7 @@ const mockQueryFees = async () => {
}
const mockQueryRoutes = async pubKey => {
action('mockQueryRoutes', pubKey)
store.set({ isQueryingRoutes: true })
await delay(2000)
const nodes = store.get('nodes')
@ -162,9 +166,10 @@ storiesOf('Containers.Pay', module)
<Modal onClose={action('clicked')}>
<State store={store}>
<Pay
width={1 / 2}
width={9 / 16}
mx="auto"
// State
// initialPayReq="lntb100n1pdaetlfpp5rkj5acj5usdlqekv3548nx5zc58tsqghm8qy6pdkrn3h37ep5aqsdqqcqzysxqyz5vq7vsxfsnak9yd0rf0zxpg9tukykxjqwef72apfwq2meg7wlz8zg0nxh3fmmc0ayv8ac5xhnlwlxajatqwnh3qwdx6uruyqn47enq9w6qplzqccc"
isProcessing={store.get('isProcessing')}
chain={store.get('chain')}
channelBalance={store.get('channelBalance')}

2
test/unit/components/Form.spec.js

@ -3,7 +3,7 @@ import { configure, shallow } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import Form from 'components/Form'
import Pay from 'components/Form/Pay'
import Pay from 'containers/Pay'
import Request from 'components/Form/Request'
configure({ adapter: new Adapter() })

87
test/unit/components/Form/Pay.spec.js

@ -1,87 +0,0 @@
import React from 'react'
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import 'jest-styled-components'
import { ThemeProvider } from 'styled-components'
import Pay from 'components/Form/Pay'
import { dark } from 'themes'
import { mountWithIntl } from '../../__helpers__/intl-enzyme-test-helper'
configure({ adapter: new Adapter() })
const defaultProps = {
payform: {
amount: 0,
payInput: '',
invoice: {},
showErrors: {}
},
currency: '',
crypto: '',
nodes: [],
ticker: {
currency: 'btc',
fiatTicker: 'USD'
},
isOnchain: false,
isLn: true,
currentAmount: 0,
usdAmount: 0,
inputCaption: '',
showPayLoadingScreen: true,
payFormIsValid: {},
currencyFilters: [],
currencyName: '',
setPayAmount: () => {},
setPayInput: () => {},
fetchInvoice: () => {},
setCurrency: () => {},
onPayAmountBlur: () => {},
onPayInputBlur: () => {},
onPaySubmit: () => {}
}
describe('Form', () => {
describe('should show the form without an input', () => {
const el = mountWithIntl(
<ThemeProvider theme={dark}>
<Pay {...defaultProps} />
</ThemeProvider>
)
it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined)
})
})
describe('should show lightning with a lightning input', () => {
const props = { ...defaultProps, isLn: true }
const el = mountWithIntl(
<ThemeProvider theme={dark}>
<Pay {...props} />
</ThemeProvider>
)
it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined)
})
})
describe('should show on-chain with an on-chain input', () => {
const props = { ...defaultProps, isOnchain: true }
const el = mountWithIntl(
<ThemeProvider theme={dark}>
<Pay {...props} />
</ThemeProvider>
)
it('should contain Pay', () => {
expect(el.find('input#paymentRequest').props.value).toBe(undefined)
})
})
})

1
test/unit/components/Pay/PaySummaryOnchain.spec.js

@ -34,6 +34,7 @@ const props = {
}
],
fiatCurrency: 'USD',
queryFees: jest.fn(),
setCryptoCurrency: jest.fn()
}

2
test/unit/components/UI/__snapshots__/Range.spec.js.snap

@ -42,7 +42,7 @@ exports[`component.UI.Range should render correctly 1`] = `
onChange={[Function]}
step={1}
type="range"
value="0"
value={0}
/>
</form>
`;

Loading…
Cancel
Save