From 7caabd4246eaa91fe1de907c0f1e208cf4de78ff Mon Sep 17 00:00:00 2001 From: vinnyhoward Date: Mon, 28 Apr 2025 17:22:15 -0600 Subject: [PATCH 01/10] feat: added network selector to the request payment flow --- app/components/UI/PaymentRequest/index.js | 49 +++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/app/components/UI/PaymentRequest/index.js b/app/components/UI/PaymentRequest/index.js index 192780a80c05..84b65a9f1b6d 100644 --- a/app/components/UI/PaymentRequest/index.js +++ b/app/components/UI/PaymentRequest/index.js @@ -45,12 +45,14 @@ import { getTicker } from '../../../util/transactions'; import { toLowerCaseEquals } from '../../../util/general'; import { utils as ethersUtils } from 'ethers'; import { ThemeContext, mockTheme } from '../../../util/theme'; -import { isTestNet } from '../../../util/networks'; +import { isTestNet, getDecimalChainId } from '../../../util/networks'; import { isTokenDetectionSupportedForNetwork } from '@metamask/assets-controllers'; import { selectChainId, selectEvmTicker, + selectNetworkConfigurations, } from '../../../selectors/networkController'; +import { selectNetworkImageSource } from '../../../selectors/networkInfos'; import { selectConversionRate, selectCurrentCurrency, @@ -59,8 +61,11 @@ import { selectTokenListArray } from '../../../selectors/tokenListController'; import { selectTokens } from '../../../selectors/tokensController'; import { selectContractExchangeRates } from '../../../selectors/tokenRatesController'; import { selectSelectedInternalAccountFormattedAddress } from '../../../selectors/accountsController'; - +import PickerNetwork from '../../../component-library/components/Pickers/PickerNetwork/PickerNetwork'; +import Routes from '../../../constants/navigation/Routes'; +import { withMetricsAwareness } from '../../../components/hooks/useMetrics'; import { RequestPaymentViewSelectors } from '../../../../e2e/selectors/Receive/RequestPaymentView.selectors'; +import { MetaMetricsEvents } from '../../../core/Analytics'; const KEYBOARD_OFFSET = 120; const createStyles = (colors) => @@ -302,6 +307,18 @@ class PaymentRequest extends PureComponent { * Object that represents the current route info like params passed to it */ route: PropTypes.object, + /** + * Metrics injected by withMetricsAwareness HOC + */ + metrics: PropTypes.object, + /** + * Network configurations + */ + networkConfigurations: PropTypes.object, + /** + * Network image source + */ + networkImageSource: PropTypes.string, }; amountInput = React.createRef(); @@ -868,13 +885,37 @@ class PaymentRequest extends PureComponent { ); }; + handleNetworkPickerPress = () => { + this.props.navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, { + screen: Routes.SHEET.NETWORK_SELECTOR, + }); + this.props.metrics.trackEvent( + this.props.metrics + .createEventBuilder(MetaMetricsEvents.NETWORK_SELECTOR_PRESSED) + .addProperties({ + chain_id: getDecimalChainId(this.props.chainId), + }) + .build(), + ); + }; + render() { const { mode } = this.state; const colors = this.context.colors || mockTheme.colors; const styles = createStyles(colors); + const networkName = + this.props.networkConfigurations?.[this.props.chainId]?.name; + const networkImageSource = this.props.networkImageSource; return ( + + + ({ ticker: selectEvmTicker(state), chainId: selectChainId(state), tokenList: selectTokenListArray(state), + networkConfigurations: selectNetworkConfigurations(state), + networkImageSource: selectNetworkImageSource(state), }); -export default connect(mapStateToProps)(PaymentRequest); +export default withMetricsAwareness(connect(mapStateToProps)(PaymentRequest)); From c6d4c39b6771a2a38c16413ff44fef6326a030cf Mon Sep 17 00:00:00 2001 From: vinnyhoward Date: Tue, 29 Apr 2025 11:24:08 -0600 Subject: [PATCH 02/10] fix: header UI --- app/components/UI/Navbar/index.js | 32 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/app/components/UI/Navbar/index.js b/app/components/UI/Navbar/index.js index 54058ef65078..6e61532d887e 100644 --- a/app/components/UI/Navbar/index.js +++ b/app/components/UI/Navbar/index.js @@ -355,9 +355,9 @@ export function getPaymentRequestOptionsTitle( const goBack = route.params?.dispatch; const innerStyles = StyleSheet.create({ headerTitleStyle: { - fontSize: 20, - color: themeColors.text.default, - ...fontStyles.normal, + // center the element + justifyContent: 'center', + alignItems: 'center', }, headerIcon: { color: themeColors.primary.default, @@ -367,11 +367,18 @@ export function getPaymentRequestOptionsTitle( shadowColor: importedColors.transparent, elevation: 0, }, + headerCloseButton: { + marginRight: 16, + }, }); return { - title, - headerTitleStyle: innerStyles.headerTitleStyle, + headerTitleAlign: 'center', + headerTitle: () => ( + + {title} + + ), headerLeft: () => goBack ? ( // eslint-disable-next-line react/jsx-no-bind @@ -390,17 +397,12 @@ export function getPaymentRequestOptionsTitle( ), headerRight: () => ( - // eslint-disable-next-line react/jsx-no-bind - navigation.pop()} - style={styles.closeButton} - > - - + style={innerStyles.headerCloseButton} + /> ), headerStyle: innerStyles.headerStyle, headerTintColor: themeColors.primary.default, From 18dbe2334590eca56797db21a36ef1be2d3eab2e Mon Sep 17 00:00:00 2001 From: vinnyhoward Date: Tue, 29 Apr 2025 13:01:16 -0600 Subject: [PATCH 03/10] fix: snaps --- .../__snapshots__/index.test.tsx.snap | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap b/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap index 430f373b1bfa..3033f8456428 100644 --- a/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap @@ -9,6 +9,85 @@ exports[`PaymentRequest renders correctly 1`] = ` } } > + + + + + + + + + + + Date: Tue, 29 Apr 2025 15:50:59 -0600 Subject: [PATCH 04/10] fix: tests --- .../UI/PaymentRequest/index.test.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/app/components/UI/PaymentRequest/index.test.tsx b/app/components/UI/PaymentRequest/index.test.tsx index 3eb9798cc25f..8fbbae265af9 100644 --- a/app/components/UI/PaymentRequest/index.test.tsx +++ b/app/components/UI/PaymentRequest/index.test.tsx @@ -11,6 +11,8 @@ import configureMockStore from 'redux-mock-store'; import { ThemeContext, mockTheme } from '../../../util/theme'; import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; import { SolScope } from '@metamask/keyring-api'; +import Routes from '../../../constants/navigation/Routes'; +import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletView.selectors'; jest.mock('react', () => ({ ...jest.requireActual('react'), @@ -131,6 +133,7 @@ const renderComponent = (props = {}) => @@ -202,4 +205,36 @@ describe('PaymentRequest', () => { expect(mockSetShowError).toHaveBeenCalledWith(true); expect(queryByText('Invalid request, please try again')).toBeTruthy(); }); + + describe('handleNetworkPickerPress', () => { + it('should navigate to network selector modal and track metrics', () => { + const mockMetrics = { + trackEvent: jest.fn(), + createEventBuilder: jest.fn().mockReturnValue({ + addProperties: jest.fn().mockReturnValue({ + build: jest.fn().mockReturnValue('builtEvent'), + }), + }), + }; + + const { getByTestId } = renderComponent({ + metrics: mockMetrics, + chainId: '0x1', + networkImageSource: '', + }); + + const networkPicker = getByTestId( + WalletViewSelectorsIDs.NAVBAR_NETWORK_PICKER, + ); + fireEvent.press(networkPicker); + + // Verify navigation + expect(mockNavigation.navigate).toHaveBeenCalledWith( + Routes.MODAL.ROOT_MODAL_FLOW, + { + screen: Routes.SHEET.NETWORK_SELECTOR, + }, + ); + }); + }); }); From e1435b86f0efdc3e344da4c36c87cbf8ed20267a Mon Sep 17 00:00:00 2001 From: vinnyhoward Date: Tue, 29 Apr 2025 16:18:38 -0600 Subject: [PATCH 05/10] fix: increase test coverage --- .../Navbar/__snapshots__/index.test.jsx.snap | 415 ++++++++++++++++++ app/components/UI/Navbar/index.js | 2 +- app/components/UI/Navbar/index.test.jsx | 93 +++- 3 files changed, 505 insertions(+), 5 deletions(-) create mode 100644 app/components/UI/Navbar/__snapshots__/index.test.jsx.snap diff --git a/app/components/UI/Navbar/__snapshots__/index.test.jsx.snap b/app/components/UI/Navbar/__snapshots__/index.test.jsx.snap new file mode 100644 index 000000000000..2b01b9a43828 --- /dev/null +++ b/app/components/UI/Navbar/__snapshots__/index.test.jsx.snap @@ -0,0 +1,415 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getPaymentRequestOptionsTitle should match snapshot with goBack function 1`] = ` + + + + + + + + + + + + + +  + + + + + + + Payment Request + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; diff --git a/app/components/UI/Navbar/index.js b/app/components/UI/Navbar/index.js index 6e61532d887e..a37f025c66ce 100644 --- a/app/components/UI/Navbar/index.js +++ b/app/components/UI/Navbar/index.js @@ -355,7 +355,6 @@ export function getPaymentRequestOptionsTitle( const goBack = route.params?.dispatch; const innerStyles = StyleSheet.create({ headerTitleStyle: { - // center the element justifyContent: 'center', alignItems: 'center', }, @@ -402,6 +401,7 @@ export function getPaymentRequestOptionsTitle( size={ButtonIconSizes.Md} onPress={() => navigation.pop()} style={innerStyles.headerCloseButton} + testID={RequestPaymentViewSelectors.BACK_BUTTON_ID} /> ), headerStyle: innerStyles.headerStyle, diff --git a/app/components/UI/Navbar/index.test.jsx b/app/components/UI/Navbar/index.test.jsx index e2625d157e6b..7dd184c10517 100644 --- a/app/components/UI/Navbar/index.test.jsx +++ b/app/components/UI/Navbar/index.test.jsx @@ -1,10 +1,12 @@ /* eslint-disable react/prop-types */ import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; +import { fireEvent } from '@testing-library/react-native'; import renderWithProvider from '../../../util/test/renderWithProvider'; import { backgroundState } from '../../../util/test/initial-root-state'; -import { getNetworkNavbarOptions } from '.'; -import { SolScope } from '@metamask/keyring-api'; +import { getNetworkNavbarOptions, getPaymentRequestOptionsTitle } from '.'; +import { mockTheme } from '../../../util/theme'; +import { RequestPaymentViewSelectors } from '../../../../e2e/selectors/Receive/RequestPaymentView.selectors'; describe('getNetworkNavbarOptions', () => { const Stack = createStackNavigator(); @@ -15,7 +17,11 @@ describe('getNetworkNavbarOptions', () => { const TestNavigator = ({ options }) => ( - options.header()} /> + null} + options={options} + /> ); @@ -28,9 +34,10 @@ describe('getNetworkNavbarOptions', () => { 'Test Title', false, mockNavigation, + mockTheme.colors ); - const { getByText, getByRole } = renderWithProvider( + const { getByText } = renderWithProvider( , { state: { @@ -46,3 +53,81 @@ describe('getNetworkNavbarOptions', () => { expect(getByText('Test Title')).toBeTruthy(); }); }); + +describe('getPaymentRequestOptionsTitle', () => { + const Stack = createStackNavigator(); + + const mockNavigation = { + pop: jest.fn(), + }; + + const TestNavigator = ({ options }) => ( + + null} + options={options} + /> + + ); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + + it('should match snapshot with goBack function', () => { + const mockGoBack = jest.fn(); + const options = getPaymentRequestOptionsTitle( + 'Payment Request', + mockNavigation, + { params: { dispatch: mockGoBack } }, + mockTheme.colors + ); + + const rendered = renderWithProvider( + , + { + state: { + engine: { + backgroundState: { + ...backgroundState, + }, + }, + }, + }, + ); + + expect(rendered).toMatchSnapshot(); + }); + + it('renders title and close button when no goBack provided', () => { + const options = getPaymentRequestOptionsTitle( + 'Payment Request', + mockNavigation, + { params: {} }, + mockTheme.colors + ); + + const { getByText, getByTestId } = renderWithProvider( + , + { + state: { + engine: { + backgroundState: { + ...backgroundState, + }, + }, + }, + }, + ); + + // Check if title is rendered + expect(getByText('Payment Request')).toBeTruthy(); + + // Check if close button works + const closeButton = getByTestId(RequestPaymentViewSelectors.BACK_BUTTON_ID); + fireEvent.press(closeButton); + expect(mockNavigation.pop).toHaveBeenCalled(); + }); +}); From ac2415fd2dc32abde6e3d0e1c0328960ecd43cd2 Mon Sep 17 00:00:00 2001 From: vinnyhoward Date: Tue, 29 Apr 2025 20:24:24 -0600 Subject: [PATCH 06/10] fix: added REMOVE_GNS flag --- app/components/UI/PaymentRequest/index.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/components/UI/PaymentRequest/index.js b/app/components/UI/PaymentRequest/index.js index 84b65a9f1b6d..e1baaff214f6 100644 --- a/app/components/UI/PaymentRequest/index.js +++ b/app/components/UI/PaymentRequest/index.js @@ -909,13 +909,15 @@ class PaymentRequest extends PureComponent { return ( - - - + {process.env.REMOVE_GNS && ( + + + + )} Date: Wed, 30 Apr 2025 15:16:41 -0600 Subject: [PATCH 07/10] fix: update snaps --- .../__snapshots__/index.test.tsx.snap | 79 ------------------- 1 file changed, 79 deletions(-) diff --git a/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap b/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap index 3033f8456428..430f373b1bfa 100644 --- a/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap @@ -9,85 +9,6 @@ exports[`PaymentRequest renders correctly 1`] = ` } } > - - - - - - - - - - - Date: Wed, 30 Apr 2025 16:06:45 -0600 Subject: [PATCH 08/10] fix: update snaps --- .../UI/Navbar/__snapshots__/index.test.jsx.snap | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/components/UI/Navbar/__snapshots__/index.test.jsx.snap b/app/components/UI/Navbar/__snapshots__/index.test.jsx.snap index 2b01b9a43828..0e1f24031b26 100644 --- a/app/components/UI/Navbar/__snapshots__/index.test.jsx.snap +++ b/app/components/UI/Navbar/__snapshots__/index.test.jsx.snap @@ -129,6 +129,7 @@ exports[`getPaymentRequestOptionsTitle should match snapshot with goBack functio > -  +  @@ -251,7 +252,13 @@ exports[`getPaymentRequestOptionsTitle should match snapshot with goBack functio "top": -1, } } + onGestureCancel={[Function]} pointerEvents="box-none" + sheetAllowedDetents="large" + sheetCornerRadius={-1} + sheetExpandsWhenScrolledToEdge={true} + sheetGrabberVisible={false} + sheetLargestUndimmedDetent="all" style={ { "bottom": 0, @@ -259,6 +266,7 @@ exports[`getPaymentRequestOptionsTitle should match snapshot with goBack functio "position": "absolute", "right": 0, "top": 0, + "zIndex": undefined, } } > From 2dc695e71fac9e8b9d6f08454662afc992e4908e Mon Sep 17 00:00:00 2001 From: vinnyhoward Date: Thu, 1 May 2025 12:59:04 -0600 Subject: [PATCH 09/10] fix: PaymentRequest tests --- .../__snapshots__/index.test.tsx.snap | 79 +++++++++++++++++++ app/components/UI/PaymentRequest/index.js | 18 ++--- .../UI/PaymentRequest/index.test.tsx | 20 ++--- 3 files changed, 99 insertions(+), 18 deletions(-) diff --git a/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap b/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap index 4c0ab1f7c0d3..541dbc7ca132 100644 --- a/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/PaymentRequest/__snapshots__/index.test.tsx.snap @@ -9,6 +9,85 @@ exports[`PaymentRequest renders correctly 1`] = ` } } > + + + + + + + + + + + - {process.env.REMOVE_GNS && ( - - - - )} + + + { }); describe('handleNetworkPickerPress', () => { - it('should navigate to network selector modal and track metrics', () => { + it('should navigate to network selector modal', () => { const mockMetrics = { trackEvent: jest.fn(), - createEventBuilder: jest.fn().mockReturnValue({ - addProperties: jest.fn().mockReturnValue({ - build: jest.fn().mockReturnValue('builtEvent'), - }), - }), + createEventBuilder: jest.fn(() => ({ + addProperties: jest.fn(() => ({ + build: jest.fn(() => 'builtEvent'), + })), + })), }; const { getByTestId } = renderComponent({ metrics: mockMetrics, chainId: '0x1', - networkImageSource: '', + networkImageSource: 'test-network-image.png', }); const networkPicker = getByTestId( WalletViewSelectorsIDs.NAVBAR_NETWORK_PICKER, ); - fireEvent.press(networkPicker); - // Verify navigation + act(() => { + fireEvent.press(networkPicker); + }); + expect(mockNavigation.navigate).toHaveBeenCalledWith( Routes.MODAL.ROOT_MODAL_FLOW, { From efc38a91db9ea00b28daaadd8427a99e63694519 Mon Sep 17 00:00:00 2001 From: vinnyhoward Date: Thu, 1 May 2025 13:49:22 -0600 Subject: [PATCH 10/10] fix: remove logs --- app/components/UI/PaymentRequest/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/components/UI/PaymentRequest/index.js b/app/components/UI/PaymentRequest/index.js index 9e12a4ba742d..84b65a9f1b6d 100644 --- a/app/components/UI/PaymentRequest/index.js +++ b/app/components/UI/PaymentRequest/index.js @@ -889,7 +889,6 @@ class PaymentRequest extends PureComponent { this.props.navigation.navigate(Routes.MODAL.ROOT_MODAL_FLOW, { screen: Routes.SHEET.NETWORK_SELECTOR, }); - console.log('TRACK EVENT PRESSED!!!'); this.props.metrics.trackEvent( this.props.metrics .createEventBuilder(MetaMetricsEvents.NETWORK_SELECTOR_PRESSED) @@ -898,7 +897,6 @@ class PaymentRequest extends PureComponent { }) .build(), ); - console.log('TRACK EVENT FINISHED!!!'); }; render() {