Skip to content

Commit

Permalink
test: Add ramps URL scheme deeplinking e2e (#12747)
Browse files Browse the repository at this point in the history
<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

The purpose of this PR is to add e2e coverage for the URL schema
deeplinks in the ramps flow.

The goal here is to ensure that we properly test the URL scheme deep
linking in these flows.

### Sell Deep link

```
Scenario 1
Given I open a sell ETH deeplink for mainnet 
Then the app should launch with the ramps build quotes page
And the token I want to sell is displayed correctly
And the amount i want to send is displayed correctly

Scenario 2
Given I open a sell deeplink on an unsupported network
Then the app should launch with the ramps build quotes page
And I am prompted to add the network
When I add the network
Then the Quotes page should be displayed 
```

### Buy deeplink Flow

```
Scenario 3
Given i deeplink to the buy eth flow on mainnet
Then the app should launch with the ramps build quotes page
And the token I want to sell is displayed correctly
And the amount i want to send is displayed correctly

Scenario 4
Given i deeplink to the buy eth flow on a popular network
Then the app should launch with the ramps build quotes page
And the token I want to sell is displayed correctly
And the amount i want to send is displayed correctly

```
## **Related issues**

Fixes:

## **Manual testing steps**

1. Go to this page...
2.
3.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **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.
  • Loading branch information
cortisiko authored Dec 20, 2024
1 parent 89b61d3 commit bea70f1
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 21 deletions.
65 changes: 46 additions & 19 deletions e2e/fixtures/fixture-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { merge } from 'lodash';
import { CustomNetworks, PopularNetworksList } from '../resources/networks.e2e';
import { CHAIN_IDS } from '@metamask/transaction-controller';

export const DEFAULT_FIXTURE_ACCOUNT = '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3';
export const DEFAULT_FIXTURE_ACCOUNT =
'0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3';

const DAPP_URL = 'localhost';

Expand Down Expand Up @@ -627,6 +628,7 @@ class FixtureBuilder {
selectedRegionAgg: null,
selectedPaymentMethodAgg: null,
getStartedAgg: false,
getStartedSell: false,
authenticationUrls: [],
activationKeys: [],
},
Expand Down Expand Up @@ -696,8 +698,9 @@ class FixtureBuilder {
const { providerConfig } = data;

// Generate a unique key for the new network client ID
const newNetworkClientId = `networkClientId${Object.keys(networkController.networkConfigurationsByChainId).length + 1
}`;
const newNetworkClientId = `networkClientId${
Object.keys(networkController.networkConfigurationsByChainId).length + 1
}`;

// Define the network configuration
const networkConfig = {
Expand Down Expand Up @@ -776,6 +779,30 @@ class FixtureBuilder {
return this;
}

withRampsSelectedRegion(region = null) {
const defaultRegion = {
currencies: ['/currencies/fiat/xcd'],
emoji: '🇱🇨',
id: '/regions/lc',
name: 'Saint Lucia',
support: { buy: true, sell: true, recurringBuy: true },
unsupported: false,
recommended: false,
detected: false,
};

// Use the provided region or fallback to the default
this.fixture.state.fiatOrders.selectedRegionAgg = region ?? defaultRegion;
return this;
}
withRampsSelectedPaymentMethod() {
const paymentType = '/payments/debit-credit-card';

// Use the provided region or fallback to the default
this.fixture.state.fiatOrders.selectedPaymentMethodAgg = paymentType;
return this;
}

/**
* Adds chain switching permission for specific chains.
* @param {string[]} chainIds - Array of chain IDs to permit (defaults to ['0x1']), other nexts like linea mainnet 0xe708
Expand Down Expand Up @@ -818,9 +845,10 @@ class FixtureBuilder {
const fixtures = this.fixture.state.engine.backgroundState;

// Generate a unique key for the new network client ID
const newNetworkClientId = `networkClientId${Object.keys(fixtures.NetworkController.networkConfigurationsByChainId)
.length + 1
}`;
const newNetworkClientId = `networkClientId${
Object.keys(fixtures.NetworkController.networkConfigurationsByChainId)
.length + 1
}`;

// Define the Ganache network configuration
const ganacheNetworkConfig = {
Expand Down Expand Up @@ -856,9 +884,10 @@ class FixtureBuilder {
const sepoliaConfig = CustomNetworks.Sepolia.providerConfig;

// Generate a unique key for the new network client ID
const newNetworkClientId = `networkClientId${Object.keys(fixtures.NetworkController.networkConfigurationsByChainId)
.length + 1
}`;
const newNetworkClientId = `networkClientId${
Object.keys(fixtures.NetworkController.networkConfigurationsByChainId)
.length + 1
}`;

// Define the Sepolia network configuration
const sepoliaNetworkConfig = {
Expand Down Expand Up @@ -908,8 +937,9 @@ class FixtureBuilder {
} = network.providerConfig;

// Generate a unique key for the new network client ID
const newNetworkClientId = `networkClientId${Object.keys(networkConfigurationsByChainId).length + 1
}`;
const newNetworkClientId = `networkClientId${
Object.keys(networkConfigurationsByChainId).length + 1
}`;

// Define the network configuration
const networkConfig = {
Expand Down Expand Up @@ -988,19 +1018,16 @@ class FixtureBuilder {
allTokens: {
[CHAIN_IDS.MAINNET]: {
[DEFAULT_FIXTURE_ACCOUNT]: tokens,
}
}
},
},
});
return this;
}

withIncomingTransactionPreferences(incomingTransactionPreferences) {
merge(
this.fixture.state.engine.backgroundState.PreferencesController,
{
showIncomingTransactions: incomingTransactionPreferences,
},
);
merge(this.fixture.state.engine.backgroundState.PreferencesController, {
showIncomingTransactions: incomingTransactionPreferences,
});
return this;
}

Expand Down
4 changes: 4 additions & 0 deletions e2e/pages/Ramps/BuildQuoteView.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ class BuildQuoteView {
async tapCancelButton() {
await Gestures.waitAndTap(this.cancelButton);
}
async tapDefaultToken(token) {
const tokenName = await Matchers.getElementByText(token);
await Gestures.waitAndTap(tokenName);
}
}

export default new BuildQuoteView();
12 changes: 12 additions & 0 deletions e2e/pages/Ramps/TokenSelectBottomSheet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Matchers from '../../utils/Matchers';
import Gestures from '../../utils/Gestures';

class TokenSelectBottomSheet {
async tapTokenByName(token) {
const tokenName = await Matchers.getElementByText(token);

await Gestures.waitAndTap(tokenName);
}
}

export default new TokenSelectBottomSheet();
6 changes: 5 additions & 1 deletion e2e/resources/blacklistURLs.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
".*phishing-detection.cx.metamask.io/.*",
".*eth.llamarpc.com/.*",
".*token-api.metaswap.codefi.network/.*",
".*gas.api.cx.metamask.io/networks/*"
".*gas.api.cx.metamask.io/networks/.*",
".*clients3.google.com/generate_204.*",
".*pulse.walletconnect.org/batch.*",
".*accounts.api.cx.metamask.io/v2/accounts/.*",
".*exp.host/--/api/v2/development-sessions/.*"
]
}
2 changes: 1 addition & 1 deletion e2e/specs/networks/add-custom-rpc.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { CustomNetworks } from '../../resources/networks.e2e';

const fixtureServer = new FixtureServer();

describe(SmokeCore('Custom RPC Tests'), () => {
describe('Custom RPC Tests', () => {
beforeAll(async () => {
await TestHelpers.reverseServerPort();
const fixture = new FixtureBuilder().build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';
import TestHelpers from '../../helpers';

import { loginToApp } from '../../viewHelper';
import { withFixtures } from '../../fixtures/fixture-helper';
import { SmokeCore } from '../../tags';
import FixtureBuilder from '../../fixtures/fixture-builder';

import SellGetStartedView from '../../pages/Ramps/SellGetStartedView';
import BuyGetStartedView from '../../pages/Ramps/BuyGetStartedView';

import Assertions from '../../utils/Assertions';
import NetworkAddedBottomSheet from '../../pages/Network/NetworkAddedBottomSheet';
import NetworkApprovalBottomSheet from '../../pages/Network/NetworkApprovalBottomSheet';
import NetworkEducationModal from '../../pages/Network/NetworkEducationModal';

describe(SmokeCore('Buy Crypto Deeplinks'), () => {
beforeAll(async () => {
await TestHelpers.reverseServerPort();
});

beforeEach(async () => {
jest.setTimeout(150000);
});

it('should deep link to onramp on Base network', async () => {
const BuyDeepLink =
'metamask://buy?chainId=8453&address=0x833589fcd6edb6e08f4c7c32d4f71b54bda02913&amount=12';

await withFixtures(
{
fixture: new FixtureBuilder().withRampsSelectedRegion().build(),
restartDevice: true,
},
async () => {
await loginToApp();
await device.sendToHome();
await device.launchApp({
url: BuyDeepLink,
});

await Assertions.checkIfVisible(
await SellGetStartedView.getStartedButton,
);

await BuyGetStartedView.tapGetStartedButton();

await Assertions.checkIfVisible(NetworkApprovalBottomSheet.container);
await NetworkApprovalBottomSheet.tapApproveButton();
await NetworkAddedBottomSheet.tapSwitchToNetwork();
await Assertions.checkIfVisible(NetworkEducationModal.container);
await NetworkEducationModal.tapGotItButton();
await Assertions.checkIfTextIsDisplayed('USD Coin');
},
);
});
});
55 changes: 55 additions & 0 deletions e2e/specs/ramps/deeplink-to-buy-flow.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict';
import TestHelpers from '../../helpers';

import { loginToApp } from '../../viewHelper';
import { withFixtures } from '../../fixtures/fixture-helper';
import { SmokeCore } from '../../tags';
import FixtureBuilder from '../../fixtures/fixture-builder';

import SellGetStartedView from '../../pages/Ramps/SellGetStartedView';
import BuyGetStartedView from '../../pages/Ramps/BuyGetStartedView';

import BuildQuoteView from '../../pages/Ramps/BuildQuoteView';
import TokenSelectBottomSheet from '../../pages/Ramps/TokenSelectBottomSheet';
import Assertions from '../../utils/Assertions';

describe(SmokeCore('Buy Crypto Deeplinks'), () => {
beforeAll(async () => {
await TestHelpers.reverseServerPort();
});

beforeEach(async () => {
jest.setTimeout(150000);
});
it('should deep link to onramp ETH', async () => {
const buyLink = 'metamask://buy?chainId=1&amount=275';

await withFixtures(
{
fixture: new FixtureBuilder()
.withRampsSelectedPaymentMethod()
.withRampsSelectedRegion()
.build(),
restartDevice: true,
},
async () => {
await loginToApp();
await device.sendToHome();
await device.launchApp({
url: buyLink,
});
await Assertions.checkIfVisible(
await SellGetStartedView.getStartedButton,
);

await BuyGetStartedView.tapGetStartedButton();
await Assertions.checkIfVisible(BuildQuoteView.getQuotesButton);
await BuildQuoteView.tapDefaultToken('Ethereum');

await TokenSelectBottomSheet.tapTokenByName('DAI');
await Assertions.checkIfTextIsDisplayed('Dai Stablecoin');
await Assertions.checkIfTextIsDisplayed('$275');
},
);
});
});
89 changes: 89 additions & 0 deletions e2e/specs/ramps/deeplink-to-sell-flow.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict';
import { loginToApp } from '../../viewHelper';

import FixtureBuilder from '../../fixtures/fixture-builder';
import { withFixtures } from '../../fixtures/fixture-helper';

import TestHelpers from '../../helpers';
import SellGetStartedView from '../../pages/Ramps/SellGetStartedView';
import { SmokeCore } from '../../tags';

import BuildQuoteView from '../../pages/Ramps/BuildQuoteView';
import Assertions from '../../utils/Assertions';
import NetworkApprovalBottomSheet from '../../pages/Network/NetworkApprovalBottomSheet';
import NetworkAddedBottomSheet from '../../pages/Network/NetworkAddedBottomSheet';
import NetworkEducationModal from '../../pages/Network/NetworkEducationModal';

describe(SmokeCore('Sell Crypto Deeplinks'), () => {
beforeAll(async () => {
await TestHelpers.reverseServerPort();
});

beforeEach(async () => {
jest.setTimeout(150000);
});
it('should deep link to offramp ETH', async () => {
const sellDeepLinkURL =
'metamask://sell?chainId=1&address=0x0000000000000000000000000000000000000000&amount=50';
const franceRegion = {
currencies: ['/currencies/fiat/eur'],
emoji: '🇫🇷',
id: '/regions/fr',
name: 'France',
support: { buy: true, sell: true, recurringBuy: true },
unsupported: false,
recommended: false,
detected: false,
};
await withFixtures(
{
fixture: new FixtureBuilder()
.withRampsSelectedRegion(franceRegion)
.build(),
restartDevice: true,
},
async () => {
await loginToApp();

await device.openURL({
url: sellDeepLinkURL,
});
await Assertions.checkIfVisible(
await SellGetStartedView.getStartedButton,
);

await SellGetStartedView.tapGetStartedButton();
await Assertions.checkIfVisible(BuildQuoteView.getQuotesButton);

await Assertions.checkIfTextIsDisplayed('50 ETH');
},
);
});
it('Should deep link to an unsupported network in the off-ramp flow', async () => {
const unsupportedNetworkSellDeepLink = 'metamask://sell?chainId=56';

await withFixtures(
{
fixture: new FixtureBuilder().withRampsSelectedRegion().build(),
restartDevice: true,
},
async () => {
await loginToApp();

await device.openURL({
url: unsupportedNetworkSellDeepLink,
});
await Assertions.checkIfVisible(
await SellGetStartedView.getStartedButton,
);

await SellGetStartedView.tapGetStartedButton();

await NetworkApprovalBottomSheet.tapApproveButton();
await NetworkAddedBottomSheet.tapSwitchToNetwork();
await Assertions.checkIfVisible(NetworkEducationModal.container);
await NetworkEducationModal.tapGotItButton();
},
);
});
});

0 comments on commit bea70f1

Please sign in to comment.