From 547cf76e4e5aa106171e7f2f489ff972467bab63 Mon Sep 17 00:00:00 2001 From: Pedro Pablo Aste Kompen Date: Fri, 8 Dec 2023 19:15:20 -0300 Subject: [PATCH] feat(ramp): add sell deeplink (#8016) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR adds the handling for the Sell flow deeplink, the same way we did for Buy flow in https://github.com/MetaMask/metamask-mobile/pull/5743: This PR adds support for metamask://sell-crypto and https://metamask.app.link/sell-crypto, these will open the sell flow. ## **Related issues** Fixes: ## **Manual testing steps** 1. Navigate to `metamask://sell-crypto` or https://metamask.app.link/sell-crypto 2. Sell flow must open ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've clearly explained what problem this PR is solving and how it is solved. - [x] I've linked related issues - [x] I've included manual testing steps - [x] I've included screenshots/recordings if applicable - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] 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. - [x] I’ve properly set the pull request status: - [x] In case it's not yet "ready for review", I've set it to "draft". - [x] In case it's "ready for review", I've changed it from "draft" to "non-draft". ## **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/constants/deeplinks.ts | 2 ++ .../DeeplinkManager/DeeplinkManager.test.ts | 6 ++++ app/core/DeeplinkManager/DeeplinkManager.ts | 4 +++ .../handleMetaMaskDeeplink.test.ts | 21 ++++++++++++++ .../ParseManager/handleMetaMaskDeeplink.ts | 2 ++ .../ParseManager/handleUniversalLink.test.ts | 28 ++++++++++++++++++- .../ParseManager/handleUniversalLink.ts | 2 ++ 7 files changed, 64 insertions(+), 1 deletion(-) diff --git a/app/constants/deeplinks.ts b/app/constants/deeplinks.ts index 79999b78f0b..20f2fc2be89 100644 --- a/app/constants/deeplinks.ts +++ b/app/constants/deeplinks.ts @@ -22,6 +22,7 @@ export enum ACTIONS { CONNECT = 'connect', ANDROID_SDK = 'bind', BUY_CRYPTO = 'buy-crypto', + SELL_CRYPTO = 'sell-crypto', EMPTY = '', } @@ -36,5 +37,6 @@ export const PREFIXES = { [ACTIONS.CONNECT]: '', [ACTIONS.ANDROID_SDK]: '', [ACTIONS.BUY_CRYPTO]: '', + [ACTIONS.SELL_CRYPTO]: '', METAMASK: 'metamask://', }; diff --git a/app/core/DeeplinkManager/DeeplinkManager.test.ts b/app/core/DeeplinkManager/DeeplinkManager.test.ts index 6c5523ae2b9..9b02346c985 100644 --- a/app/core/DeeplinkManager/DeeplinkManager.test.ts +++ b/app/core/DeeplinkManager/DeeplinkManager.test.ts @@ -99,6 +99,12 @@ describe('DeeplinkManager', () => { deeplinkManager._handleBuyCrypto(); expect(mockNavigation.navigate).toHaveBeenCalledWith('RampBuy'); }); + + it('should handle sell crypto action correctly', () => { + deeplinkManager._handleSellCrypto(); + expect(mockNavigation.navigate).toHaveBeenCalledWith('RampSell'); + }); + it('should parse deeplinks correctly', () => { const url = 'http://example.com'; const browserCallBack = jest.fn(); diff --git a/app/core/DeeplinkManager/DeeplinkManager.ts b/app/core/DeeplinkManager/DeeplinkManager.ts index 7ce15adec40..47f1119a776 100644 --- a/app/core/DeeplinkManager/DeeplinkManager.ts +++ b/app/core/DeeplinkManager/DeeplinkManager.ts @@ -71,6 +71,10 @@ class DeeplinkManager { this.navigation.navigate(Routes.RAMP.BUY); } + _handleSellCrypto() { + this.navigation.navigate(Routes.RAMP.SELL); + } + parse( url: string, { diff --git a/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.test.ts b/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.test.ts index 52c3f4173a6..65313b62f04 100644 --- a/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.test.ts +++ b/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.test.ts @@ -21,6 +21,7 @@ jest.mock('../../../core/NativeModules', () => ({ describe('handleMetaMaskProtocol', () => { const mockParse = jest.fn(); const mockHandleBuyCrypto = jest.fn(); + const mockHandleSellCrypto = jest.fn(); const mockHandleBrowserUrl = jest.fn(); const mockConnectToChannel = jest.fn(); const mockGetConnections = jest.fn(); @@ -37,6 +38,7 @@ describe('handleMetaMaskProtocol', () => { const instance = { parse: mockParse, _handleBuyCrypto: mockHandleBuyCrypto, + _handleSellCrypto: mockHandleSellCrypto, _handleBrowserUrl: mockHandleBrowserUrl, } as unknown as DeeplinkManager; @@ -216,4 +218,23 @@ describe('handleMetaMaskProtocol', () => { expect(mockHandleBuyCrypto).toHaveBeenCalled(); }); }); + + describe('when url start with ${PREFIXES.METAMASK}${ACTIONS.SELL_CRYPTO}', () => { + beforeEach(() => { + url = `${PREFIXES.METAMASK}${ACTIONS.SELL_CRYPTO}`; + }); + + it('should call _handleSellCrypto', () => { + handleMetaMaskDeeplink({ + instance, + handled, + params, + url, + origin, + wcURL, + }); + + expect(mockHandleSellCrypto).toHaveBeenCalled(); + }); + }); }); diff --git a/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.ts b/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.ts index d98cbd43080..2c472c468f9 100644 --- a/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.ts +++ b/app/core/DeeplinkManager/ParseManager/handleMetaMaskDeeplink.ts @@ -80,6 +80,8 @@ export function handleMetaMaskDeeplink({ }); } else if (url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.BUY_CRYPTO}`)) { instance._handleBuyCrypto(); + } else if (url.startsWith(`${PREFIXES.METAMASK}${ACTIONS.SELL_CRYPTO}`)) { + instance._handleSellCrypto(); } } diff --git a/app/core/DeeplinkManager/ParseManager/handleUniversalLink.test.ts b/app/core/DeeplinkManager/ParseManager/handleUniversalLink.test.ts index f7fedbd77af..00731d19e2d 100644 --- a/app/core/DeeplinkManager/ParseManager/handleUniversalLink.test.ts +++ b/app/core/DeeplinkManager/ParseManager/handleUniversalLink.test.ts @@ -22,6 +22,7 @@ jest.mock('../../../core/NativeModules', () => ({ describe('handleUniversalLinks', () => { const mockParse = jest.fn(); const mockHandleBuyCrypto = jest.fn(); + const mockHandleSellCrypto = jest.fn(); const mockHandleBrowserUrl = jest.fn(); const mockConnectToChannel = jest.fn(); const mockGetConnections = jest.fn(); @@ -37,6 +38,7 @@ describe('handleUniversalLinks', () => { const instance = { parse: mockParse, _handleBuyCrypto: mockHandleBuyCrypto, + _handleSellCrypto: mockHandleSellCrypto, _handleBrowserUrl: mockHandleBrowserUrl, } as unknown as DeeplinkManager; @@ -306,8 +308,32 @@ describe('handleUniversalLinks', () => { }); }); + describe('ACTIONS.SELL_CRYPTO', () => { + it('should call instance._handleSellCrypto if action is ACTIONS.SELL_CRYPTO', () => { + urlObj = { + hostname: AppConstants.MM_UNIVERSAL_LINK_HOST, + pathname: `/${ACTIONS.SELL_CRYPTO}/additional/path`, + href: 'test-href', + } as ReturnType['urlObj']; + + handleUniversalLink({ + instance, + handled, + urlObj, + params, + browserCallBack: mockBrowserCallBack, + origin, + wcURL, + url, + }); + + expect(handled).toHaveBeenCalled(); + expect(mockHandleSellCrypto).toHaveBeenCalledTimes(1); + }); + }); + describe('default condition', () => { - it('should call instance._handleBrowserUrl if action is not ACTIONS.BUY_CRYPTO', () => { + it('should call instance._handleBrowserUrl if action is not ACTIONS.BUY_CRYPTO or ACTIONS.SELL_CRYPTO', () => { urlObj = { hostname: AppConstants.MM_UNIVERSAL_LINK_HOST, pathname: `/other-action/additional/path`, diff --git a/app/core/DeeplinkManager/ParseManager/handleUniversalLink.ts b/app/core/DeeplinkManager/ParseManager/handleUniversalLink.ts index 2281f2a3fb5..2fd5c5bd1ca 100644 --- a/app/core/DeeplinkManager/ParseManager/handleUniversalLink.ts +++ b/app/core/DeeplinkManager/ParseManager/handleUniversalLink.ts @@ -91,6 +91,8 @@ function handleUniversalLink({ instance.parse(deeplinkUrl, { browserCallBack, origin }); } else if (action === ACTIONS.BUY_CRYPTO) { instance._handleBuyCrypto(); + } else if (action === ACTIONS.SELL_CRYPTO) { + instance._handleSellCrypto(); } else { // If it's our universal link or Apple store deep link don't open it in the browser if (