wallet-stack 1.0.0-alpha.132 → 1.0.0-alpha.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/locales/base/translation.json +5 -0
  2. package/package.json +1 -2
  3. package/src/analytics/Events.tsx +3 -10
  4. package/src/analytics/Properties.tsx +9 -25
  5. package/src/analytics/docs.ts +11 -8
  6. package/src/app/ErrorMessages.ts +0 -7
  7. package/src/identity/actions.ts +1 -97
  8. package/src/identity/contactMapping.test.ts +3 -28
  9. package/src/identity/contactMapping.ts +2 -88
  10. package/src/identity/reducer.ts +0 -77
  11. package/src/identity/saga.ts +2 -85
  12. package/src/identity/selectors.ts +0 -2
  13. package/src/images/Images.ts +2 -0
  14. package/src/images/assets/minipay.png +0 -0
  15. package/src/images/assets/minipay@1.5x.png +0 -0
  16. package/src/images/assets/minipay@2x.png +0 -0
  17. package/src/images/assets/minipay@3x.png +0 -0
  18. package/src/images/assets/minipay@4x.png +0 -0
  19. package/src/images/assets/valora.png +0 -0
  20. package/src/images/assets/valora@1.5x.png +0 -0
  21. package/src/images/assets/valora@2x.png +0 -0
  22. package/src/images/assets/valora@3x.png +0 -0
  23. package/src/images/assets/valora@4x.png +0 -0
  24. package/src/index.d.ts +0 -1
  25. package/src/navigator/Navigator.tsx +4 -14
  26. package/src/navigator/Screens.tsx +1 -2
  27. package/src/navigator/types.tsx +4 -6
  28. package/src/qrcode/utils.test.tsx +4 -96
  29. package/src/qrcode/utils.ts +5 -114
  30. package/src/redux/migrations.test.ts +13 -0
  31. package/src/redux/migrations.ts +4 -0
  32. package/src/redux/store.test.ts +1 -2
  33. package/src/redux/store.ts +1 -1
  34. package/src/send/SelectRecipientAddress.test.tsx +146 -0
  35. package/src/send/SelectRecipientAddress.tsx +166 -0
  36. package/src/send/SendSelectRecipient.test.tsx +16 -87
  37. package/src/send/SendSelectRecipient.tsx +12 -27
  38. package/src/send/actions.ts +0 -26
  39. package/src/send/saga.ts +1 -6
  40. package/src/components/AccountNumberCard.tsx +0 -23
  41. package/src/components/ErrorMessageInline.tsx +0 -78
  42. package/src/components/SingleDigitInput.tsx +0 -53
  43. package/src/icons/HamburgerCard.tsx +0 -55
  44. package/src/identity/saga.test.ts +0 -103
  45. package/src/identity/secureSend.ts +0 -171
  46. package/src/send/ValidateRecipientAccount.test.tsx +0 -182
  47. package/src/send/ValidateRecipientAccount.tsx +0 -392
  48. package/src/send/ValidateRecipientIntro.test.tsx +0 -61
  49. package/src/send/ValidateRecipientIntro.tsx +0 -136
  50. package/src/send/__snapshots__/ValidateRecipientAccount.test.tsx.snap +0 -777
@@ -1,78 +0,0 @@
1
- import React from 'react'
2
- import { WithTranslation } from 'react-i18next'
3
- import { StyleSheet, Text, View } from 'react-native'
4
- import { connect } from 'react-redux'
5
- import { hideAlert } from 'src/alert/actions'
6
- import { ErrorDisplayType } from 'src/alert/reducer'
7
- import { ErrorMessages } from 'src/app/ErrorMessages'
8
- import { withTranslation } from 'src/i18n'
9
- import { RootState } from 'src/redux/reducers'
10
- import colors from 'src/styles/colors'
11
- import { typeScale } from 'src/styles/fonts'
12
-
13
- const DISMISS_DEFAULT = 5
14
-
15
- interface StateProps {
16
- displayMethod: ErrorDisplayType | null
17
- }
18
-
19
- interface OwnProps {
20
- error?: ErrorMessages | null
21
- dismissAfter?: number
22
- }
23
-
24
- interface DispatchProps {
25
- hideAlert: typeof hideAlert
26
- }
27
-
28
- const mapStateToProps = (state: RootState): StateProps => {
29
- const displayMethod = state.alert ? state.alert.displayMethod : null
30
- return {
31
- displayMethod,
32
- }
33
- }
34
-
35
- const mapDispatchToProps = {
36
- hideAlert,
37
- }
38
-
39
- type Props = DispatchProps & OwnProps & WithTranslation & StateProps
40
-
41
- function ErrorMessageInline(props: Props) {
42
- const { error, displayMethod, dismissAfter, t } = props
43
-
44
- // Want to initiate/cleanup a timer for each new error
45
- React.useEffect(() => {
46
- const timer = window.setTimeout(props.hideAlert, (dismissAfter || DISMISS_DEFAULT) * 1000)
47
- return () => window.clearTimeout(timer)
48
- }, [error])
49
-
50
- // Keep the space empty when there isn't an inline error
51
- // displayMethod lives in redux store and we want to be able to use this component
52
- // without populating it so much check if other types are already displayed
53
- // rather than if INLINE is
54
- if (!error || displayMethod === ErrorDisplayType.BANNER) {
55
- return <View style={dismissAfter !== null && styles.errorContainer} />
56
- }
57
-
58
- return (
59
- <View style={dismissAfter !== null && styles.errorContainer}>
60
- <Text style={styles.errorMessage}>{t(error)} </Text>
61
- </View>
62
- )
63
- }
64
-
65
- const styles = StyleSheet.create({
66
- errorContainer: {
67
- height: 64,
68
- },
69
- errorMessage: {
70
- ...typeScale.bodySmall,
71
- color: colors.errorPrimary,
72
- },
73
- })
74
-
75
- export default connect<StateProps, DispatchProps, OwnProps, RootState>(
76
- (state: RootState) => mapStateToProps,
77
- mapDispatchToProps
78
- )(withTranslation<Props>()(ErrorMessageInline))
@@ -1,53 +0,0 @@
1
- import * as React from 'react'
2
- import { StyleSheet } from 'react-native'
3
- import TextInput from 'src/components/TextInput'
4
- import colors from 'src/styles/colors'
5
-
6
- export interface SingleDigitInputProps {
7
- inputValue: string
8
- inputPlaceholder: string
9
- onInputChange: (value: string) => void
10
- testID?: string
11
- forwardedRef: any
12
- }
13
-
14
- type Props = SingleDigitInputProps
15
-
16
- // Multiline enabled as to handle unexpected cursor behavior
17
- // https://github.com/facebook/react-native/issues/28794#issuecomment-877769852
18
-
19
- export function SingleDigitInput({
20
- inputValue,
21
- inputPlaceholder,
22
- onInputChange,
23
- testID,
24
- forwardedRef,
25
- }: Props) {
26
- return (
27
- <TextInput
28
- multiline={true}
29
- textAlign={'center'}
30
- value={inputValue}
31
- placeholder={inputPlaceholder}
32
- onChangeText={onInputChange}
33
- maxLength={1}
34
- showClearButton={false}
35
- style={styles.codeInput}
36
- testID={testID}
37
- ref={forwardedRef}
38
- />
39
- )
40
- }
41
-
42
- const styles = StyleSheet.create({
43
- codeInput: {
44
- borderColor: colors.borderPrimary,
45
- borderRadius: 3,
46
- borderWidth: 1,
47
- flex: 0,
48
- backgroundColor: colors.backgroundPrimary,
49
- height: 50,
50
- width: 50,
51
- marginVertical: 5,
52
- },
53
- })
@@ -1,55 +0,0 @@
1
- import * as React from 'react'
2
- import { StyleSheet, View } from 'react-native'
3
- import { Line, Svg } from 'react-native-svg'
4
- import { default as Colors, default as colors } from 'src/styles/colors'
5
- import globalStyles from 'src/styles/styles'
6
-
7
- function HamburgerCard() {
8
- return (
9
- <View style={styles.container}>
10
- <Svg testID="Hamburger" width="32" height="32" viewBox="0 0 32 32" fill="none">
11
- <Line
12
- x1="7.25"
13
- y1="9.75"
14
- x2="24.75"
15
- y2="9.75"
16
- stroke={Colors.contentPrimary}
17
- strokeWidth="2.5"
18
- strokeLinecap="round"
19
- />
20
- <Line
21
- x1="7.25"
22
- y1="15.75"
23
- x2="24.75"
24
- y2="15.75"
25
- stroke={Colors.contentPrimary}
26
- strokeWidth="2.5"
27
- strokeLinecap="round"
28
- />
29
- <Line
30
- x1="7.25"
31
- y1="21.75"
32
- x2="24.75"
33
- y2="21.75"
34
- stroke={Colors.contentPrimary}
35
- strokeWidth="2.5"
36
- strokeLinecap="round"
37
- />
38
- </Svg>
39
- </View>
40
- )
41
- }
42
-
43
- const styles = StyleSheet.create({
44
- container: {
45
- backgroundColor: colors.backgroundPrimary,
46
- ...globalStyles.softShadowLight,
47
- alignItems: 'center',
48
- justifyContent: 'center',
49
- borderRadius: 4,
50
- height: 32,
51
- width: 32,
52
- },
53
- })
54
-
55
- export default React.memo(HamburgerCard)
@@ -1,103 +0,0 @@
1
- import { expectSaga } from 'redux-saga-test-plan'
2
- import { select } from 'redux-saga/effects'
3
- import { showErrorInline } from 'src/alert/actions'
4
- import { ErrorMessages } from 'src/app/ErrorMessages'
5
- import {
6
- Actions,
7
- ValidateRecipientAddressAction,
8
- validateRecipientAddressSuccess,
9
- } from 'src/identity/actions'
10
- import { AddressValidationType } from 'src/identity/reducer'
11
- import { watchValidateRecipientAddress } from 'src/identity/saga'
12
- import { e164NumberToAddressSelector } from 'src/identity/selectors'
13
- import { currentAccountSelector } from 'src/web3/selectors'
14
- import {
15
- mockAccount,
16
- mockAccount2,
17
- mockAccountInvite,
18
- mockE164NumberInvite,
19
- mockE164NumberToAddress,
20
- mockInvitableRecipient2,
21
- } from 'test/values'
22
-
23
- describe(watchValidateRecipientAddress, () => {
24
- beforeAll(() => {
25
- jest.useRealTimers()
26
- })
27
-
28
- afterEach(() => {
29
- jest.clearAllMocks()
30
- })
31
-
32
- it('full validation fails if the inputted address does not belong to the recipient', async () => {
33
- const validateAction: ValidateRecipientAddressAction = {
34
- type: Actions.VALIDATE_RECIPIENT_ADDRESS,
35
- userInputOfFullAddressOrLastFourDigits: mockAccount2,
36
- addressValidationType: AddressValidationType.FULL,
37
- recipient: mockInvitableRecipient2,
38
- }
39
-
40
- await expectSaga(watchValidateRecipientAddress)
41
- .provide([
42
- [select(currentAccountSelector), mockAccount],
43
- [select(e164NumberToAddressSelector), mockE164NumberToAddress],
44
- ])
45
- .dispatch(validateAction)
46
- .put(showErrorInline(ErrorMessages.ADDRESS_VALIDATION_NO_MATCH))
47
- .run()
48
- })
49
-
50
- it('full validation succeeds if the inputted address belongs to the recipient', async () => {
51
- const validateAction: ValidateRecipientAddressAction = {
52
- type: Actions.VALIDATE_RECIPIENT_ADDRESS,
53
- userInputOfFullAddressOrLastFourDigits: mockAccountInvite,
54
- addressValidationType: AddressValidationType.FULL,
55
- recipient: mockInvitableRecipient2,
56
- }
57
-
58
- await expectSaga(watchValidateRecipientAddress)
59
- .provide([
60
- [select(currentAccountSelector), mockAccount],
61
- [select(e164NumberToAddressSelector), mockE164NumberToAddress],
62
- ])
63
- .dispatch(validateAction)
64
- .put(validateRecipientAddressSuccess(mockE164NumberInvite, mockAccountInvite.toLowerCase()))
65
- .run()
66
- })
67
-
68
- it('partial validation fails if the inputted address does not belong to the recipient', async () => {
69
- const validateAction: ValidateRecipientAddressAction = {
70
- type: Actions.VALIDATE_RECIPIENT_ADDRESS,
71
- userInputOfFullAddressOrLastFourDigits: mockAccount2.slice(-4),
72
- addressValidationType: AddressValidationType.PARTIAL,
73
- recipient: mockInvitableRecipient2,
74
- }
75
-
76
- await expectSaga(watchValidateRecipientAddress)
77
- .provide([
78
- [select(currentAccountSelector), mockAccount],
79
- [select(e164NumberToAddressSelector), mockE164NumberToAddress],
80
- ])
81
- .dispatch(validateAction)
82
- .put(showErrorInline(ErrorMessages.ADDRESS_VALIDATION_NO_MATCH))
83
- .run()
84
- })
85
-
86
- it('partial validation succeeds if the inputted address belongs to the recipient', async () => {
87
- const validateAction: ValidateRecipientAddressAction = {
88
- type: Actions.VALIDATE_RECIPIENT_ADDRESS,
89
- userInputOfFullAddressOrLastFourDigits: mockAccountInvite.slice(-4),
90
- addressValidationType: AddressValidationType.PARTIAL,
91
- recipient: mockInvitableRecipient2,
92
- }
93
-
94
- await expectSaga(watchValidateRecipientAddress)
95
- .provide([
96
- [select(currentAccountSelector), mockAccount],
97
- [select(e164NumberToAddressSelector), mockE164NumberToAddress],
98
- ])
99
- .dispatch(validateAction)
100
- .put(validateRecipientAddressSuccess(mockE164NumberInvite, mockAccountInvite.toLowerCase()))
101
- .run()
102
- })
103
- })
@@ -1,171 +0,0 @@
1
- import { ErrorMessages } from 'src/app/ErrorMessages'
2
- import { AddressValidationType, SecureSendPhoneNumberMapping } from 'src/identity/reducer'
3
- import { Recipient, recipientHasNumber } from 'src/recipients/recipient'
4
- import Logger from 'src/utils/Logger'
5
-
6
- const TAG = 'identity/secureSend'
7
-
8
- // Given addresses can be added and deleted we can't rely on changes in length to signal address changes
9
- // Not sure if address order in array is consistent so not assuming it
10
- function newAddressesAdded(oldAddresses: string[], newAddresses: string[]) {
11
- const oldAddressesSorted = oldAddresses.sort()
12
- const newAddressesSorted = newAddresses.sort()
13
-
14
- for (let i = 0; i < newAddressesSorted.length; i += 1) {
15
- if (oldAddressesSorted[i] !== newAddressesSorted[i]) {
16
- return true
17
- }
18
- }
19
-
20
- return false
21
- }
22
-
23
- function last4DigitsAreUnique(addressArr: string[]) {
24
- const last4DigitArr = addressArr.map((address) => address.slice(-4).toLowerCase())
25
- const last4DigitSet = new Set()
26
- last4DigitArr.forEach((endDigits) => last4DigitSet.add(endDigits))
27
- return last4DigitArr.length === last4DigitSet.size
28
- }
29
-
30
- // Check if there are multiple addresses but somehow a preferred address hasn't been set yet
31
- // Should never happen in production but makes the change backwards compatible for testing env
32
- function accidentallyBypassedValidation(
33
- newAddresses: string[] | null,
34
- e164Number: string,
35
- secureSendPhoneNumberMapping: SecureSendPhoneNumberMapping
36
- ) {
37
- const validatedAddress =
38
- secureSendPhoneNumberMapping[e164Number] && secureSendPhoneNumberMapping[e164Number].address
39
- return newAddresses && newAddresses.length > 1 && !validatedAddress
40
- }
41
-
42
- export function checkIfValidationRequired(
43
- oldAddresses: string[],
44
- possibleAddresses: string[],
45
- userAddress: string,
46
- secureSendPhoneNumberMapping: SecureSendPhoneNumberMapping,
47
- e164Number: string
48
- ) {
49
- // No validation needed if there is only 1 possible address
50
- if (possibleAddresses.length < 2) {
51
- return AddressValidationType.NONE
52
- }
53
-
54
- if (
55
- newAddressesAdded(oldAddresses, possibleAddresses) ||
56
- accidentallyBypassedValidation(possibleAddresses, e164Number, secureSendPhoneNumberMapping)
57
- ) {
58
- Logger.debug(TAG, 'Address needs to be validated by user')
59
- // Adding user's address so they don't mistakenly verify with last 4 digits of their own address
60
- if (last4DigitsAreUnique([userAddress, ...possibleAddresses])) {
61
- return AddressValidationType.PARTIAL
62
- }
63
- return AddressValidationType.FULL
64
- }
65
-
66
- return AddressValidationType.NONE
67
- }
68
-
69
- function hasSpecialChars(address: string, addressValidationType: AddressValidationType) {
70
- const regex = new RegExp('[^0-9A-Za-z]', 'g')
71
- const cleanedAddress = address.replace(regex, '')
72
-
73
- if (cleanedAddress !== address) {
74
- const errorMessage =
75
- addressValidationType === AddressValidationType.FULL
76
- ? ErrorMessages.ADDRESS_VALIDATION_FULL_POORLY_FORMATTED
77
- : ErrorMessages.ADDRESS_VALIDATION_PARTIAL_POORLY_FORMATTED
78
- throw Error(errorMessage)
79
- }
80
- }
81
-
82
- function validateFullAddressAndReturnMatch(
83
- userInputtedAddress: string,
84
- possibleRecievingAddresses: string[],
85
- userAddress: string
86
- ) {
87
- if (userInputtedAddress.length !== 42 || userInputtedAddress.slice(0, 2) !== '0x') {
88
- throw Error(ErrorMessages.ADDRESS_VALIDATION_FULL_POORLY_FORMATTED)
89
- }
90
-
91
- if (userInputtedAddress === userAddress) {
92
- throw Error(ErrorMessages.ADDRESS_VALIDATION_FULL_OWN_ADDRESS)
93
- }
94
-
95
- if (!possibleRecievingAddresses.includes(userInputtedAddress)) {
96
- throw Error(ErrorMessages.ADDRESS_VALIDATION_NO_MATCH)
97
- }
98
-
99
- return userInputtedAddress
100
- }
101
-
102
- function validatePartialAddressAndReturnMatch(
103
- lastFourDigitsOfUserInputtedAddress: string,
104
- possibleRecievingAddresses: string[],
105
- userAddress: string
106
- ) {
107
- if (lastFourDigitsOfUserInputtedAddress.length !== 4) {
108
- throw Error(ErrorMessages.ADDRESS_VALIDATION_PARTIAL_POORLY_FORMATTED)
109
- }
110
-
111
- if (lastFourDigitsOfUserInputtedAddress === userAddress.slice(-4)) {
112
- throw Error(ErrorMessages.ADDRESS_VALIDATION_PARTIAL_OWN_ADDRESS)
113
- }
114
-
115
- const targetAddress = possibleRecievingAddresses.find(
116
- (address) => address.slice(-4) === lastFourDigitsOfUserInputtedAddress
117
- )
118
-
119
- if (!targetAddress) {
120
- throw Error(ErrorMessages.ADDRESS_VALIDATION_NO_MATCH)
121
- }
122
-
123
- return targetAddress
124
- }
125
-
126
- export function validateAndReturnMatch(
127
- userInputOfFullAddressOrLastFourDigits: string,
128
- possibleRecievingAddresses: string[],
129
- userAddress: string,
130
- addressValidationType: AddressValidationType
131
- ) {
132
- const userInput = userInputOfFullAddressOrLastFourDigits.toLowerCase()
133
- const possibleAddresses = possibleRecievingAddresses.map((address) => address.toLowerCase())
134
- const userOwnAddress = userAddress.toLowerCase()
135
-
136
- hasSpecialChars(userInputOfFullAddressOrLastFourDigits, addressValidationType)
137
-
138
- if (addressValidationType === AddressValidationType.FULL) {
139
- return validateFullAddressAndReturnMatch(userInput, possibleAddresses, userOwnAddress)
140
- }
141
- return validatePartialAddressAndReturnMatch(userInput, possibleAddresses, userOwnAddress)
142
- }
143
-
144
- export function getAddressValidationType(
145
- recipient: Recipient,
146
- secureSendPhoneNumberMapping: SecureSendPhoneNumberMapping
147
- ) {
148
- const e164PhoneNumber = recipient.e164PhoneNumber
149
-
150
- if (
151
- !e164PhoneNumber ||
152
- !secureSendPhoneNumberMapping[e164PhoneNumber] ||
153
- !secureSendPhoneNumberMapping[e164PhoneNumber].addressValidationType
154
- ) {
155
- return AddressValidationType.NONE
156
- }
157
-
158
- return secureSendPhoneNumberMapping[e164PhoneNumber].addressValidationType
159
- }
160
-
161
- export function getSecureSendAddress(
162
- recipient: Recipient,
163
- secureSendPhoneNumberMapping: SecureSendPhoneNumberMapping
164
- ) {
165
- const e164PhoneNumber = recipientHasNumber(recipient) ? recipient.e164PhoneNumber : undefined
166
- if (!e164PhoneNumber || !secureSendPhoneNumberMapping[e164PhoneNumber]) {
167
- return undefined
168
- }
169
-
170
- return secureSendPhoneNumberMapping[e164PhoneNumber].address
171
- }
@@ -1,182 +0,0 @@
1
- import { fireEvent, render } from '@testing-library/react-native'
2
- import * as React from 'react'
3
- import { Provider } from 'react-redux'
4
- import { SendOrigin } from 'src/analytics/types'
5
- import { AddressValidationType } from 'src/identity/reducer'
6
- import { navigate } from 'src/navigator/NavigationService'
7
- import { Screens } from 'src/navigator/Screens'
8
- import ValidateRecipientAccount from 'src/send/ValidateRecipientAccount'
9
- import { createMockStore, getMockStackScreenProps } from 'test/utils'
10
- import {
11
- mockAccountInvite,
12
- mockCusdAddress,
13
- mockE164NumberInvite,
14
- mockInvitableRecipient2,
15
- } from 'test/values'
16
-
17
- describe('ValidateRecipientAccount', () => {
18
- beforeEach(() => {
19
- jest.clearAllMocks()
20
- })
21
-
22
- it('renders correctly when full validation required', () => {
23
- const store = createMockStore({
24
- identity: {
25
- secureSendPhoneNumberMapping: {
26
- [mockE164NumberInvite]: {
27
- addressValidationType: AddressValidationType.FULL,
28
- validationSuccessful: false,
29
- },
30
- },
31
- },
32
- })
33
- const tree = render(
34
- <Provider store={store}>
35
- <ValidateRecipientAccount
36
- {...getMockStackScreenProps(Screens.ValidateRecipientAccount, {
37
- recipient: mockInvitableRecipient2,
38
- origin: SendOrigin.AppSendFlow,
39
- })}
40
- />
41
- </Provider>
42
- )
43
- expect(tree).toMatchSnapshot()
44
- })
45
-
46
- it('renders correctly when partial validation required', () => {
47
- const store = createMockStore({
48
- identity: {
49
- secureSendPhoneNumberMapping: {
50
- [mockE164NumberInvite]: {
51
- addressValidationType: AddressValidationType.PARTIAL,
52
- validationSuccessful: false,
53
- },
54
- },
55
- },
56
- })
57
- const tree = render(
58
- <Provider store={store}>
59
- <ValidateRecipientAccount
60
- {...getMockStackScreenProps(Screens.ValidateRecipientAccount, {
61
- recipient: mockInvitableRecipient2,
62
- origin: SendOrigin.AppSendFlow,
63
- })}
64
- />
65
- </Provider>
66
- )
67
- expect(tree).toMatchSnapshot()
68
- })
69
-
70
- it('typing correct last four of account enables submit button', () => {
71
- const store = createMockStore({
72
- identity: {
73
- secureSendPhoneNumberMapping: {
74
- [mockE164NumberInvite]: {
75
- addressValidationType: AddressValidationType.PARTIAL,
76
- validationSuccessful: false,
77
- },
78
- },
79
- },
80
- })
81
- const tree = render(
82
- <Provider store={store}>
83
- <ValidateRecipientAccount
84
- {...getMockStackScreenProps(Screens.ValidateRecipientAccount, {
85
- recipient: mockInvitableRecipient2,
86
- origin: SendOrigin.AppSendFlow,
87
- })}
88
- />
89
- </Provider>
90
- )
91
- expect(tree.getByTestId('ConfirmAccountButton')).toBeDisabled()
92
- fireEvent.changeText(tree.getByTestId('SingleDigitInput/digit0'), mockCusdAddress[38])
93
- fireEvent.changeText(tree.getByTestId('SingleDigitInput/digit1'), mockCusdAddress[39])
94
- fireEvent.changeText(tree.getByTestId('SingleDigitInput/digit2'), mockCusdAddress[40])
95
- fireEvent.changeText(tree.getByTestId('SingleDigitInput/digit3'), mockCusdAddress[41])
96
- expect(tree.getByTestId('ConfirmAccountButton')).not.toBeDisabled()
97
- })
98
-
99
- it('enables submit button for full validation', () => {
100
- const store = createMockStore({
101
- identity: {
102
- secureSendPhoneNumberMapping: {
103
- [mockE164NumberInvite]: {
104
- addressValidationType: AddressValidationType.FULL,
105
- validationSuccessful: false,
106
- },
107
- },
108
- },
109
- })
110
- const tree = render(
111
- <Provider store={store}>
112
- <ValidateRecipientAccount
113
- {...getMockStackScreenProps(Screens.ValidateRecipientAccount, {
114
- recipient: mockInvitableRecipient2,
115
- origin: SendOrigin.AppSendFlow,
116
- })}
117
- />
118
- </Provider>
119
- )
120
-
121
- expect(tree.getByTestId('ConfirmAccountButton')).toBeDisabled()
122
- fireEvent.changeText(
123
- tree.getByTestId('ValidateRecipientAccount/TextInput'),
124
- mockCusdAddress[38]
125
- )
126
- expect(tree.getByTestId('ConfirmAccountButton')).not.toBeDisabled()
127
- })
128
-
129
- it('navigates to send enter amount with validated address when validation is successful', () => {
130
- const store = createMockStore({
131
- identity: {
132
- secureSendPhoneNumberMapping: {
133
- [mockE164NumberInvite]: {
134
- addressValidationType: AddressValidationType.NONE,
135
- validationSuccessful: false,
136
- },
137
- },
138
- },
139
- })
140
-
141
- const props = getMockStackScreenProps(Screens.ValidateRecipientAccount, {
142
- recipient: mockInvitableRecipient2,
143
- origin: SendOrigin.AppSendFlow,
144
- })
145
-
146
- const tree = render(
147
- <Provider store={store}>
148
- <ValidateRecipientAccount {...props} />
149
- </Provider>
150
- )
151
-
152
- const updatedStore = createMockStore({
153
- identity: {
154
- secureSendPhoneNumberMapping: {
155
- [mockE164NumberInvite]: {
156
- addressValidationType: AddressValidationType.NONE,
157
- address: mockAccountInvite,
158
- validationSuccessful: true,
159
- },
160
- },
161
- },
162
- })
163
-
164
- tree.rerender(
165
- <Provider store={updatedStore}>
166
- <ValidateRecipientAccount {...props} />
167
- </Provider>
168
- )
169
-
170
- expect(navigate).toHaveBeenCalledWith(Screens.SendEnterAmount, {
171
- origin: SendOrigin.AppSendFlow,
172
- isFromScan: false,
173
- defaultTokenIdOverride: undefined,
174
- forceTokenId: undefined,
175
- recipient: {
176
- ...mockInvitableRecipient2,
177
- address: mockAccountInvite,
178
- },
179
- isMiniPayRecipient: false,
180
- })
181
- })
182
- })