From 9e11cf640e0f86bec715b404c769597191d1970d Mon Sep 17 00:00:00 2001 From: Curtis David Date: Fri, 20 Dec 2024 11:06:05 -0500 Subject: [PATCH 1/2] test: E2E to reveal SRP on error boundary screen (#12805) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** To catch any regressions in the crash screen’s reveal SRP flow, we should add an end-to-end (e2e) test. Since revealing the Secret Recovery Phrase is the user’s last chance to view and securely save it, this test will check that each step in the flow works smoothly: from entering your password to viewing your SRP. ``` Scenario: A user can reveal their SRP while on the crash screen Given I encounter the crash screen When I tap linked text to reveal my SRP And I enter an invalid password Then I should see the appropriate error When I enter the correct password Then I can see my SRP ``` ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- app/components/Views/ErrorBoundary/index.js | 11 +++- .../Login/__snapshots__/index.test.tsx.snap | 22 ++++++++ e2e/pages/Browser/TestDApp.js | 10 +++- .../ErrorBoundaryView/ErrorBoundaryView.js | 21 +++++++ .../Network/NetworkApprovalBottomSheet.js | 12 +++- .../ErrorBoundaryView.selectors.js | 9 +++ .../error-boundary-srp-backup.spec.js | 56 +++++++++++++++++++ 7 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 e2e/pages/ErrorBoundaryView/ErrorBoundaryView.js create mode 100644 e2e/selectors/ErrorBoundary/ErrorBoundaryView.selectors.js create mode 100644 e2e/specs/accounts/error-boundary-srp-backup.spec.js diff --git a/app/components/Views/ErrorBoundary/index.js b/app/components/Views/ErrorBoundary/index.js index a0c7af42270..603ebc4f312 100644 --- a/app/components/Views/ErrorBoundary/index.js +++ b/app/components/Views/ErrorBoundary/index.js @@ -40,7 +40,7 @@ import { } from '../../../components/hooks/useMetrics'; import AppConstants from '../../../core/AppConstants'; import { useSelector } from 'react-redux'; - +import { isTest } from '../../../util/test/utils'; // eslint-disable-next-line import/no-commonjs const WarningIcon = require('./warning-icon.png'); @@ -246,7 +246,6 @@ export const Fallback = (props) => { captureSentryFeedback({ sentryId: props.sentryId, comments: feedback }); Alert.alert(strings('error_screen.bug_report_thanks')); }; - return ( @@ -270,6 +269,14 @@ export const Fallback = (props) => { } /> + + {isTest && ( + + + {strings('error_screen.save_seedphrase_2')} + + + )} {strings('error_screen.error_message')} diff --git a/app/components/Views/Login/__snapshots__/index.test.tsx.snap b/app/components/Views/Login/__snapshots__/index.test.tsx.snap index 4beae542b9e..599cee47dee 100644 --- a/app/components/Views/Login/__snapshots__/index.test.tsx.snap +++ b/app/components/Views/Login/__snapshots__/index.test.tsx.snap @@ -186,6 +186,28 @@ exports[`Login should render correctly 1`] = ` + + + save your Secret Recovery Phrase + + { + beforeAll(async () => { + jest.setTimeout(2500000); + await TestHelpers.reverseServerPort(); + }); + + it('should trigger error boundary screen to reveal SRP', async () => { + await withFixtures( + { + dapp: true, + fixture: new FixtureBuilder() + .withGanacheNetwork() + .withPermissionControllerConnectedToTestDapp() + .build(), + restartDevice: true, + ganacheOptions: defaultGanacheOptions, + }, + async () => { + await loginToApp(); + + await TabBarComponent.tapBrowser(); + await Browser.navigateToTestDApp(); + + await TestDApp.tapInvalidSigButton(); + await Assertions.checkIfVisible(ErrorBoundaryView.title); + await ErrorBoundaryView.tapSRPLinkText(); + + await RevealSecretRecoveryPhrase.enterPasswordToRevealSecretCredential( + PASSWORD, + ); + // If the following step fails, ensure you are using a test build with tap and hold to reveal animation disabled + await RevealSecretRecoveryPhrase.tapToReveal(); + await Assertions.checkIfVisible(RevealSecretRecoveryPhrase.container); + + await Assertions.checkIfTextIsDisplayed(defaultGanacheOptions.mnemonic); + }, + ); + }); +}); From 59d72d8a63c7bf3a794ac612ffdcdd6dd4bc944c Mon Sep 17 00:00:00 2001 From: Salim TOUBAL Date: Fri, 20 Dec 2024 17:29:43 +0100 Subject: [PATCH 2/2] fix: fix ramp flow (#12796) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** ## **Related issues** Fixes: #11783 ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Pedro Pablo Aste Kompen --- .../NetworkSwitcher/NetworkSwitcher.test.tsx | 6 +++-- .../Views/NetworkSwitcher/NetworkSwitcher.tsx | 23 ++++++++++++++----- .../CustomNetworkView/CustomNetwork.tsx | 3 ++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx index f9921440bd4..5ae44ae8a59 100644 --- a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx +++ b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx @@ -181,6 +181,7 @@ const mockuseRampSDKInitialValues: Partial = { isBuy: true, isSell: false, rampType: RampType.BUY, + setIntent: jest.fn(), }; let mockUseRampSDKValues: Partial = { @@ -275,12 +276,12 @@ describe('NetworkSwitcher View', () => { expect(cancelButtons3.length).toBe(1); }); - it('switches network by calling setProviderType', async () => { + it('switches network by calling setActiveNetwork', async () => { render(NetworkSwitcher); const lineaNetworkText = screen.getByText('Linea Main Network'); fireEvent.press(lineaNetworkText); expect( - (Engine.context.NetworkController.setProviderType as jest.Mock).mock + (Engine.context.NetworkController.setActiveNetwork as jest.Mock).mock .calls, ).toMatchInlineSnapshot(` [ @@ -290,6 +291,7 @@ describe('NetworkSwitcher View', () => { ] `); + jest.clearAllMocks(); render(NetworkSwitcher); const polygonNetworkTest = screen.getByText('Polygon Mainnet'); fireEvent.press(polygonNetworkTest); diff --git a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx index bd41a45d241..6dbf9289fb9 100644 --- a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx +++ b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx @@ -59,7 +59,7 @@ function NetworkSwitcher() { } = useRampNetworksDetail(); const supportedNetworks = useSelector(getRampNetworks); const [isCurrentNetworkRampSupported] = useRampNetwork(); - const { selectedChainId, isBuy, intent } = useRampSDK(); + const { selectedChainId, isBuy, intent, setIntent } = useRampSDK(); const networkConfigurations = useSelector(selectNetworkConfigurations); const [networkToBeAdded, setNetworkToBeAdded] = useState(); @@ -145,7 +145,7 @@ function NetworkSwitcher() { const switchToMainnet = useCallback( (type: 'mainnet' | 'linea-mainnet') => { const { NetworkController } = Engine.context; - NetworkController.setProviderType(type); + NetworkController.setActiveNetwork(type); navigateToGetStarted(); }, [navigateToGetStarted], @@ -173,13 +173,23 @@ function NetworkSwitcher() { const handleNetworkPress = useCallback( (networkConfiguration) => { + setIntent((prevIntent) => ({ + ...prevIntent, + chainId: networkConfiguration.chainId, + })); + + const networkConfigurationWithHexChainId = { + ...networkConfiguration, + chainId: toHex(networkConfiguration.chainId), + }; + if (networkConfiguration.isAdded) { - switchNetwork(networkConfiguration); + switchNetwork(networkConfigurationWithHexChainId); } else { - setNetworkToBeAdded(networkConfiguration); + setNetworkToBeAdded(networkConfigurationWithHexChainId); } }, - [switchNetwork], + [setIntent, switchNetwork], ); const handleIntentChainId = useCallback( @@ -199,7 +209,8 @@ function NetworkSwitcher() { (networkConfiguration) => { const isAdded = Object.values(networkConfigurations).some( (savedNetwork) => - savedNetwork.chainId === networkConfiguration.chainId, + toHex(savedNetwork.chainId) === + toHex(networkConfiguration.chainId), ); return { ...networkConfiguration, diff --git a/app/components/Views/Settings/NetworksSettings/NetworkSettings/CustomNetworkView/CustomNetwork.tsx b/app/components/Views/Settings/NetworksSettings/NetworkSettings/CustomNetworkView/CustomNetwork.tsx index fa297b73a7b..afb9f8421e8 100644 --- a/app/components/Views/Settings/NetworksSettings/NetworkSettings/CustomNetworkView/CustomNetwork.tsx +++ b/app/components/Views/Settings/NetworksSettings/NetworkSettings/CustomNetworkView/CustomNetwork.tsx @@ -3,6 +3,7 @@ import NetworkModals from '../../../../../UI/NetworkModal'; import { View, TouchableOpacity } from 'react-native'; import { useSelector } from 'react-redux'; import WarningIcon from 'react-native-vector-icons/FontAwesome'; +import { toHex } from '@metamask/controller-utils'; import CustomText from '../../../../../Base/Text'; import EmptyPopularList from '../emptyList'; import { useNavigation } from '@react-navigation/native'; @@ -46,7 +47,7 @@ const CustomNetwork = ({ // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any (savedNetwork: any) => - savedNetwork.chainId === networkConfiguration.chainId, + toHex(savedNetwork.chainId) === toHex(networkConfiguration.chainId), ); return { ...networkConfiguration,