Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: UX: Multichain: Add test for mutiple dapp confirmation order #25536

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions test/e2e/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ const closeSRPReveal = async (driver) => {
const DAPP_HOST_ADDRESS = '127.0.0.1:8080';
const DAPP_URL = `http://${DAPP_HOST_ADDRESS}`;
const DAPP_ONE_URL = 'http://127.0.0.1:8081';
const DAPP_TWO_URL = 'http://127.0.0.1:8082';

const openDapp = async (driver, contract = null, dappURL = DAPP_URL) => {
return contract
Expand Down Expand Up @@ -1121,6 +1122,7 @@ module.exports = {
DAPP_HOST_ADDRESS,
DAPP_URL,
DAPP_ONE_URL,
DAPP_TWO_URL,
TEST_SEED_PHRASE,
TEST_SEED_PHRASE_TWO,
PRIVATE_KEY,
Expand Down
270 changes: 236 additions & 34 deletions test/e2e/tests/request-queuing/ui.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { strict: assert } = require('assert');
const { Browser } = require('selenium-webdriver');
const FixtureBuilder = require('../../fixture-builder');
const {
withFixtures,
Expand All @@ -11,21 +12,33 @@ const {
defaultGanacheOptions,
switchToNotificationWindow,
veryLargeDelayMs,
DAPP_TWO_URL,
} = require('../../helpers');
const { PAGES } = require('../../webdriver/driver');

async function openDappAndSwitchChain(driver, dappUrl, chainId) {
const notificationWindowIndex = chainId ? 4 : 3;
// Window handle adjustments will need to be made for Non-MV3 Firefox
// due to OffscreenDocument. Additionally Firefox continually bombs
// with a "NoSuchWindowError: Browsing context has been discarded" whenever
// we try to open a third dapp, so this test run in Firefox will
// validate two dapps instead of 3
const IS_FIREFOX = process.env.SELENIUM_BROWSER === Browser.FIREFOX;

async function openDappAndSwitchChain(
driver,
dappUrl,
chainId,
notificationWindowIndex = 3,
) {
// Open the dapp
await openDapp(driver, undefined, dappUrl);
await driver.delay(regularDelayMs);

// Connect to the dapp
await driver.findClickableElement({ text: 'Connect', tag: 'button' });
await driver.clickElement('#connectButton');
await driver.delay(regularDelayMs);

await switchToNotificationWindow(driver, notificationWindowIndex);

await driver.clickElement({
text: 'Next',
tag: 'button',
Expand Down Expand Up @@ -62,39 +75,99 @@ async function openDappAndSwitchChain(driver, dappUrl, chainId) {
}
}

async function selectDappClickSendGetNetwork(driver, dappUrl) {
async function selectDappClickSend(driver, dappUrl) {
await driver.switchToWindowWithUrl(dappUrl);
// Windows: MetaMask, TestDapp1, TestDapp2
const expectedWindowHandles = 3;
await driver.waitUntilXWindowHandles(expectedWindowHandles);
const currentWindowHandles = await driver.getAllWindowHandles();
await driver.clickElement('#sendButton');
}

// Under mv3, we don't need to add to the current number of window handles
// because the offscreen document returned by getAllWindowHandles provides
// an extra window handle
const newWindowHandles = await driver.waitUntilXWindowHandles(
process.env.ENABLE_MV3 === 'true' || process.env.ENABLE_MV3 === undefined
? currentWindowHandles.length
: currentWindowHandles.length + 1,
);
const [newNotificationWindowHandle] = newWindowHandles.filter(
(h) => !currentWindowHandles.includes(h),
);
await driver.switchToWindow(newNotificationWindowHandle);
async function switchToNotificationPopoverValidateDetails(
driver,
expectedDetails,
) {
// Switches to the MetaMask Dialog window for confirmation
const windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog, windowHandles);

// Get UI details
const networkPill = await driver.findElement(
'[data-testid="network-display"]',
);
const networkText = await networkPill.getText();
await driver.clickElement({ css: 'button', text: 'Reject' });
return networkText;
const originElement = await driver.findElement(
'.confirm-page-container-summary__origin bdi',
);
const originText = await originElement.getText();

// Get state details
const notificationWindowState = await driver.executeScript(() =>
window.stateHooks?.getCleanAppState?.(),
);
const { chainId } = notificationWindowState.metamask.providerConfig;

// Ensure accuracy
validateConfirmationDetails(
{ networkText, originText, chainId },
expectedDetails,
);
}

async function rejectTransaction(driver) {
await driver.clickElement({ tag: 'button', text: 'Reject' });
}

async function confirmTransaction(driver) {
await driver.clickElement({ tag: 'button', text: 'Confirm' });
}

function validateConfirmationDetails(
{ chainId, networkText, originText },
expected,
) {
assert.equal(chainId, expected.chainId);
assert.equal(networkText, expected.networkText);
assert.equal(originText, expected.originText);
}

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

async function validateBalanceAndActivity(
driver,
expectedBalance,
expectedActivityEntries = 1,
) {
// Ensure the balance changed if the the transaction was confirmed
await driver.waitForSelector({
css: '[data-testid="eth-overview__primary-currency"] .currency-display-component__text',
text: expectedBalance,
});

// Ensure there's an activity entry of "Send" and "Confirmed"
if (expectedActivityEntries) {
await driver.clickElement('[data-testid="account-overview__activity-tab"]');
assert.equal(
(
await driver.findElements({
css: '[data-testid="activity-list-item-action"]',
text: 'Send',
})
).length,
expectedActivityEntries,
);
assert.equal(
(await driver.findElements('.transaction-status-label--confirmed'))
.length,
expectedActivityEntries,
);
}
}

describe('Request-queue UI changes', function () {
it('UI should show network specific to domain @no-mmi', async function () {
const port = 8546;
const chainId = 1338;
const chainId = 1338; // 0x53a
await withFixtures(
{
dapp: true,
Expand Down Expand Up @@ -126,30 +199,159 @@ describe('Request-queue UI changes', function () {
await openDappAndSwitchChain(driver, DAPP_URL);

// Open the second dapp and switch chains
await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x1');
await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x53a', 4);

// Go to wallet fullscreen, ensure that the global network changed to Ethereum Mainnet
await driver.switchToWindowWithTitle(
WINDOW_TITLES.ExtensionInFullScreenView,
);
await driver.findElement({
css: '[data-testid="network-display"]',
text: 'Ethereum Mainnet',
text: 'Localhost 8546',
});

// Go to the first dapp, ensure it uses localhost
const dappOneNetworkPillText = await selectDappClickSendGetNetwork(
driver,
DAPP_URL,
);
assert.equal(dappOneNetworkPillText, 'Localhost 8545');
await selectDappClickSend(driver, DAPP_URL);
await switchToNotificationPopoverValidateDetails(driver, {
chainId: '0x539',
networkText: 'Localhost 8545',
originText: DAPP_URL,
});
await rejectTransaction(driver);

// Go to the second dapp, ensure it uses Ethereum Mainnet
const dappTwoNetworkPillText = await selectDappClickSendGetNetwork(
driver,
DAPP_ONE_URL,
await selectDappClickSend(driver, DAPP_ONE_URL);
await switchToNotificationPopoverValidateDetails(driver, {
chainId: '0x53a',
networkText: 'Localhost 8546',
originText: DAPP_ONE_URL,
});
await rejectTransaction(driver);
},
);
});

it('handles three confirmations on three confirmations concurrently @no-mmi', async function () {
const port = 8546;
const chainId = 1338; // 0x53a
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withNetworkControllerTripleGanache()
.withPreferencesControllerUseRequestQueueEnabled()
.withSelectedNetworkControllerPerDomain()
.build(),
ganacheOptions: {
...defaultGanacheOptions,
concurrent: [
// Ganache for network 1
{
port,
chainId,
ganacheOptions2: defaultGanacheOptions,
},
// Ganache for network 3
{
port: 7777,
chainId: 1000,
ganacheOptions2: defaultGanacheOptions,
},
],
},
dappOptions: { numberOfDapps: 3 },
title: this.test.fullTitle(),
},
async ({ driver }) => {
await unlockWallet(driver);

// Navigate to extension home screen
await driver.navigate(PAGES.HOME);

// Open the first dapp
await openDappAndSwitchChain(driver, DAPP_URL);

// Open the second dapp and switch chains
await openDappAndSwitchChain(driver, DAPP_ONE_URL, '0x53a', 4);

if (!IS_FIREFOX) {
// Open the third dapp and switch chains
await openDappAndSwitchChain(driver, DAPP_TWO_URL, '0x3e8', 5);
}

// Trigger a send confirmation on the first dapp, do not confirm or reject
await selectDappClickSend(driver, DAPP_URL);

// Trigger a send confirmation on the second dapp, do not confirm or reject
await selectDappClickSend(driver, DAPP_ONE_URL);

if (!IS_FIREFOX) {
// Trigger a send confirmation on the third dapp, do not confirm or reject
await selectDappClickSend(driver, DAPP_TWO_URL);
}

// Switch to the Notification window, ensure first transaction still showing
await switchToNotificationPopoverValidateDetails(driver, {
chainId: '0x539',
networkText: 'Localhost 8545',
originText: DAPP_URL,
});

// Confirm transaction, wait for first confirmation window to close, second to display
await confirmTransaction(driver);
await driver.delay(veryLargeDelayMs);

// Switch to the new Notification window, ensure second transaction showing
await switchToNotificationPopoverValidateDetails(driver, {
chainId: '0x53a',
networkText: 'Localhost 8546',
originText: DAPP_ONE_URL,
});

// Reject this transaction, wait for second confirmation window to close, third to display
await rejectTransaction(driver);
await driver.delay(veryLargeDelayMs);

if (!IS_FIREFOX) {
// Switch to the new Notification window, ensure third transaction showing
await switchToNotificationPopoverValidateDetails(driver, {
chainId: '0x3e8',
networkText: 'Localhost 7777',
originText: DAPP_TWO_URL,
});

// Confirm transaction
await confirmTransaction(driver);
}

// With first and last confirmations confirmed, and second rejected,
// Ensure only first and last network balances were affected
await driver.switchToWindowWithTitle(
WINDOW_TITLES.ExtensionInFullScreenView,
);

// Wait for transaction to be completed on final confirmation
await driver.delay(veryLargeDelayMs);

if (!IS_FIREFOX) {
// Start on the last joined network, whose send transaction was just confirmed
await validateBalanceAndActivity(driver, '24.9998');
}

// Switch to second network, ensure full balance
await switchToNetworkByName(driver, 'Localhost 8546');
await validateBalanceAndActivity(driver, '25', 0);

// Turn on test networks in Networks menu so Localhost 8545 is available
await driver.clickElement('[data-testid="network-display"]');
await driver.clickElement('.mm-modal-content__dialog .toggle-button');
await driver.clickElement(
'.mm-modal-content__dialog button[aria-label="Close"]',
);
assert.equal(dappTwoNetworkPillText, 'Ethereum Mainnet');

// Switch to first network, whose send transaction was just confirmed
await switchToNetworkByName(driver, 'Localhost 8545');
await validateBalanceAndActivity(driver, '24.9998');
},
);
});
Expand Down