Skip to content

Commit

Permalink
test: UX: Multichain: Add E2E for signaling network change from Netwo…
Browse files Browse the repository at this point in the history
…rk menu to dapp, Autoswitching networks (#25765)

## **Description**

Adds an end to end tests for:
* Autoswitching networks upon UI load
* Autoswitching networks upon last confirmation rejection/confirmation
* Signaling network change from extension to dapp

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25765?quickstart=1)

## **Related issues**

Fixes:

## **Manual testing steps**

1. N/A, this is a E2E addition

## **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
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.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-extension/blob/develop/.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
darkwing authored Jul 22, 2024
1 parent 721a38b commit 5540fdf
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 6 deletions.
18 changes: 18 additions & 0 deletions app/scripts/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,24 @@ async function start() {
}

async function queryCurrentActiveTab(windowType) {
// Shims the activeTab for E2E test runs only if the
// "activeTabOrigin" querystring key=value is set
if (process.env.IN_TEST) {
const searchParams = new URLSearchParams(window.location.search);
const mockUrl = searchParams.get('activeTabOrigin');
if (mockUrl) {
const { origin, protocol } = new URL(mockUrl);
const returnUrl = {
id: 'mock-site',
title: 'Mock Site',
url: mockUrl,
origin,
protocol,
};
return returnUrl;
}
}

// At the time of writing we only have the `activeTab` permission which means
// that this query will only succeed in the popup context (i.e. after a "browserAction")
if (windowType !== ENVIRONMENT_TYPE_POPUP) {
Expand Down
184 changes: 181 additions & 3 deletions test/e2e/tests/request-queuing/ui.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { strict: assert } = require('assert');
const { Browser } = require('selenium-webdriver');
const { Browser, until } = require('selenium-webdriver');
const FixtureBuilder = require('../../fixture-builder');
const {
withFixtures,
Expand Down Expand Up @@ -124,10 +124,16 @@ async function confirmTransaction(driver) {
}

async function switchToNetworkByName(driver, networkName) {
await driver.clickElement('[data-testid="network-display"]');
await driver.clickElement('.mm-picker-network');
await driver.clickElement(`[data-testid="${networkName}"]`);
}

async function openPopupWithActiveTabOrigin(driver, origin) {
await driver.openNewPage(
`${driver.extensionUrl}/${PAGES.POPUP}.html?activeTabOrigin=${origin}`,
);
}

async function validateBalanceAndActivity(
driver,
expectedBalance,
Expand Down Expand Up @@ -428,6 +434,178 @@ describe('Request-queue UI changes', function () {
);
});

it('should signal from UI to dapp the network change @no-mmi', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPreferencesControllerUseRequestQueueEnabled()
.withSelectedNetworkControllerPerDomain()
.build(),
ganacheOptions: defaultGanacheOptions,
title: this.test.fullTitle(),
driverOptions: { constrainWindowSize: true },
},
async ({ driver }) => {
// Navigate to extension home screen
await unlockWallet(driver);

// Open the first dapp which starts on chain '0x539
await openDappAndSwitchChain(driver, DAPP_URL);

// Ensure the dapp starts on the correct network
await driver.wait(
until.elementTextContains(
await driver.findElement('#chainId'),
'0x539',
),
);

// Open the popup with shimmed activeTabOrigin
await openPopupWithActiveTabOrigin(driver, DAPP_URL);

// Switch to mainnet
await switchToNetworkByName(driver, 'Ethereum Mainnet');

// Switch back to the Dapp tab
await driver.switchToWindowWithUrl(DAPP_URL);

// Check to make sure the dapp network changed
await driver.wait(
until.elementTextContains(
await driver.findElement('#chainId'),
'0x1',
),
);
},
);
});

it('should autoswitch networks to the last used network for domain', async function () {
const port = 8546;
const chainId = 1338;
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withNetworkControllerDoubleGanache()
.withPreferencesControllerUseRequestQueueEnabled()
.withSelectedNetworkControllerPerDomain()
.build(),
ganacheOptions: {
...defaultGanacheOptions,
concurrent: [
{
port,
chainId,
ganacheOptions2: defaultGanacheOptions,
},
],
},
dappOptions: { numberOfDapps: 2 },
title: this.test.fullTitle(),
},
async ({ driver }) => {
// Open fullscreen
await unlockWallet(driver);

// Open the first dapp which starts on chain '0x539
await openDappAndSwitchChain(driver, DAPP_URL);

// Open tab 2, switch to Ethereum Mainnet
await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4);

// Open the popup with shimmed activeTabOrigin
await openPopupWithActiveTabOrigin(driver, DAPP_URL);

// Ensure network was reset to original
await driver.findElement({
css: '.multichain-app-header__contents--avatar-network .mm-text',
text: 'Localhost 8545',
});

// Ensure toast is shown to the user
await driver.findElement({
css: '.toast-text',
text: 'Localhost 8545 is now active on 127.0.0.1:8080',
});
},
);
});

it('should autoswitch networks when last confirmation from another network is rejected', async function () {
const port = 8546;
const chainId = 1338;

await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withNetworkControllerDoubleGanache()
.withPreferencesControllerUseRequestQueueEnabled()
.withSelectedNetworkControllerPerDomain()
.build(),
ganacheOptions: {
...defaultGanacheOptions,
concurrent: [
{
port,
chainId,
ganacheOptions2: defaultGanacheOptions,
},
],
},
dappOptions: { numberOfDapps: 2 },
title: this.test.fullTitle(),
driverOptions: { constrainWindowSize: true },
},
async ({ driver }) => {
await unlockWallet(driver);

// Open the first dapp which starts on chain '0x539
await openDappAndSwitchChain(driver, DAPP_URL);

// Open tab 2, switch to Ethereum Mainnet
await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1', 4);
await driver.waitForSelector({
css: '.error-message-text',
text: 'You are on the Ethereum Mainnet.',
});
await driver.delay(veryLargeDelayMs);

// Start a Send on Ethereum Mainnet
await driver.clickElement('#sendButton');
await driver.delay(regularDelayMs);

// Open the popup with shimmed activeTabOrigin
await openPopupWithActiveTabOrigin(driver, DAPP_URL);

// Ensure the confirmation pill shows Ethereum Mainnet
await driver.waitForSelector({
css: '[data-testid="network-display"]',
text: 'Ethereum Mainnet',
});

// Reject the confirmation
await driver.clickElement(
'[data-testid="page-container-footer-cancel"]',
);

// Wait for network to automatically change to localhost
await driver.waitForSelector({
css: '.multichain-app-header__contents--avatar-network .mm-text',
text: 'Localhost 8545',
});

// Ensure toast is shown to the user
await driver.waitForSelector({
css: '.toast-text',
text: 'Localhost 8545 is now active on 127.0.0.1:8080',
});
},
);
});

it('should gracefully handle network connectivity failure for signatures @no-mmi', async function () {
const port = 8546;
const chainId = 1338;
Expand Down Expand Up @@ -471,7 +649,7 @@ describe('Request-queue UI changes', function () {
await driver.switchToWindowWithTitle(
WINDOW_TITLES.ExtensionInFullScreenView,
);
await driver.findElement({
await driver.waitForSelector({
css: '[data-testid="network-display"]',
text: 'Ethereum Mainnet',
});
Expand Down
11 changes: 10 additions & 1 deletion test/e2e/webdriver/chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ function getProxyServer(proxyPort) {
* A wrapper around a {@code WebDriver} instance exposing Chrome-specific functionality
*/
class ChromeDriver {
static async build({ openDevToolsForTabs, port, proxyPort }) {
static async build({
openDevToolsForTabs,
constrainWindowSize,
port,
proxyPort,
}) {
const args = [
`--proxy-server=${getProxyServer(proxyPort)}`, // Set proxy in the way that doesn't interfere with Selenium Manager
'--disable-features=OptimizationGuideModelDownloading,OptimizationHintsFetching,OptimizationTargetPrediction,OptimizationHints,NetworkTimeServiceQuerying', // Stop chrome from calling home so much (auto-downloads of AI models; time sync)
Expand All @@ -42,6 +47,10 @@ class ChromeDriver {
args.push('--auto-open-devtools-for-tabs');
}

if (constrainWindowSize) {
args.push('--window-size=320,600');
}

args.push('--log-level=3');
args.push('--enable-logging');

Expand Down
5 changes: 3 additions & 2 deletions test/e2e/webdriver/firefox.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ class FirefoxDriver {
* @param {object} options - the options for the build
* @param options.responsive
* @param options.port
* @param options.constrainWindowSize
* @param options.proxyPort
* @returns {Promise<{driver: !ThenableWebDriver, extensionUrl: string, extensionId: string}>}
*/
static async build({ responsive, port, proxyPort }) {
static async build({ responsive, port, constrainWindowSize, proxyPort }) {
const templateProfile = fs.mkdtempSync(TEMP_PROFILE_PATH_PREFIX);
const options = new firefox.Options().setProfile(templateProfile);

Expand Down Expand Up @@ -99,7 +100,7 @@ class FirefoxDriver {
const extensionId = await fxDriver.installExtension('dist/firefox');
const internalExtensionId = await fxDriver.getInternalId();

if (responsive) {
if (responsive || constrainWindowSize) {
await driver.manage().window().setRect({ width: 320, height: 600 });
}

Expand Down
2 changes: 2 additions & 0 deletions test/e2e/webdriver/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const FirefoxDriver = require('./firefox');
async function buildWebDriver({
openDevToolsForTabs,
port,
constrainWindowSize,
timeOut,
proxyPort,
} = {}) {
Expand All @@ -18,6 +19,7 @@ async function buildWebDriver({
} = await buildBrowserWebDriver(browser, {
openDevToolsForTabs,
port,
constrainWindowSize,
proxyPort,
});
const driver = new Driver(seleniumDriver, browser, extensionUrl, timeOut);
Expand Down

0 comments on commit 5540fdf

Please sign in to comment.