1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 09:23:21 +01:00

Implement tests for multiple service worker restarts on the mv3 build (#19293)

* implement multiple restart tests

* remove console.logs

* fix

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* close stale prs

* revert chromedriver version

* delete code leftover

* remove unlockWallet method

---------

Co-authored-by: Brad Decker <bhdecker84@gmail.com>
This commit is contained in:
Pedro Figueiredo 2023-06-20 11:17:08 +01:00 committed by GitHub
parent 8a85d220e2
commit 66c9654244
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 535 additions and 25 deletions

View File

@ -126,6 +126,7 @@ async function defineAndRunBuildTasks() {
'Promise',
'JSON',
'Date',
'Proxy',
// globals sentry needs to function
'__SENTRY__',
'appState',

View File

@ -530,32 +530,97 @@ const locateAccountBalanceDOM = async (driver, ganacheServer) => {
text: `${balance} ETH`,
});
};
const DEFAULT_PRIVATE_KEY =
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC';
const WALLET_PASSWORD = 'correct horse battery staple';
const restartServiceWorker = async (driver) => {
const serviceWorkerElements = await driver.findElements({
text: 'terminate',
tag: 'span',
});
// 1st one is app-init.js; while 2nd one is service-worker.js
await serviceWorkerElements[1].click();
const DEFAULT_GANACHE_OPTIONS = {
accounts: [
{
secretKey: DEFAULT_PRIVATE_KEY,
balance: generateETHBalance(25),
},
],
};
const generateGanacheOptions = (overrides) => ({
...DEFAULT_GANACHE_OPTIONS,
...overrides,
});
async function waitForAccountRendered(driver) {
await driver.waitForSelector(
'[data-testid="eth-overview__primary-currency"]',
);
}
const WINDOW_TITLES = Object.freeze({
ExtensionInFullScreenView: 'MetaMask',
TestDApp: 'E2E Test Dapp',
Notification: 'MetaMask Notification',
ServiceWorkerSettings: 'Inspect with Chrome Developer Tools',
InstalledExtensions: 'Extensions',
});
const login = async (driver) => {
const unlockWallet = async (driver) => {
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
};
const logInWithBalanceValidation = async (driver, ganacheServer) => {
await login(driver);
await unlockWallet(driver);
await assertAccountBalanceForDOM(driver, ganacheServer);
};
function roundToXDecimalPlaces(number, decimalPlaces) {
return Math.round(number * 10 ** decimalPlaces) / 10 ** decimalPlaces;
}
function generateRandNumBetween(x, y) {
const min = Math.min(x, y);
const max = Math.max(x, y);
const randomNumber = Math.random() * (max - min) + min;
return randomNumber;
}
async function switchToWindow(driver, windowTitle) {
const windowHandles = await driver.getAllWindowHandles();
return await driver.switchToWindowWithTitle(windowTitle, windowHandles);
}
async function sleepSeconds(sec) {
return new Promise((resolve) => setTimeout(resolve, sec * 1000));
}
async function terminateServiceWorker(driver) {
await driver.openNewPage(SERVICE_WORKER_URL);
await driver.waitForSelector({
text: 'Service workers',
tag: 'button',
});
await driver.clickElement({
text: 'Service workers',
tag: 'button',
});
const serviceWorkerElements = await driver.findElements({
text: 'terminate',
tag: 'span',
});
// 1st one is app-init.js; while 2nd one is service-worker.js
await serviceWorkerElements[serviceWorkerElements.length - 1].click();
const serviceWorkerTab = await switchToWindow(
driver,
WINDOW_TITLES.ServiceWorkerSettings,
);
await driver.closeWindowHandle(serviceWorkerTab);
}
module.exports = {
DAPP_URL,
DAPP_ONE_URL,
@ -583,10 +648,19 @@ module.exports = {
defaultGanacheOptions,
sendTransaction,
findAnotherAccountFromAccountList,
login,
unlockWallet,
logInWithBalanceValidation,
assertAccountBalanceForDOM,
locateAccountBalanceDOM,
restartServiceWorker,
waitForAccountRendered,
generateGanacheOptions,
WALLET_PASSWORD,
WINDOW_TITLES,
DEFAULT_GANACHE_OPTIONS,
generateETHBalance,
roundToXDecimalPlaces,
generateRandNumBetween,
switchToWindow,
sleepSeconds,
terminateServiceWorker,
};

View File

@ -0,0 +1,427 @@
const { strict: assert } = require('assert');
const {
withFixtures,
openDapp,
generateGanacheOptions,
WALLET_PASSWORD,
WINDOW_TITLES,
DEFAULT_GANACHE_OPTIONS,
generateETHBalance,
roundToXDecimalPlaces,
generateRandNumBetween,
switchToWindow,
sleepSeconds,
terminateServiceWorker,
unlockWallet,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('MV3 - Restart service worker multiple times', function () {
it('Simple simple send flow within full screen view should still be usable', async function () {
const initialBalance = roundToXDecimalPlaces(
generateRandNumBetween(10, 100),
4,
);
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: generateGanacheOptions({
accounts: [
{
secretKey: DEFAULT_GANACHE_OPTIONS.accounts[0].secretKey,
balance: generateETHBalance(initialBalance),
},
],
}),
title: this.test.title,
driverOptions: { openDevToolsForTabs: true },
},
async ({ driver }) => {
await driver.navigate();
await unlockWallet(driver, WALLET_PASSWORD);
await assertETHBalance(driver, initialBalance);
// first send ETH and then terminate SW
const RECIPIENT_ADDRESS = '0x985c30949c92df7a0bd42e0f3e3d539ece98db24';
const amountFirstTx = roundToXDecimalPlaces(
generateRandNumBetween(0.5, 2),
4,
);
const gasFeesFirstTx = await simpleSendETH(
driver,
amountFirstTx,
RECIPIENT_ADDRESS,
);
const totalAfterFirstTx = roundToXDecimalPlaces(
initialBalance - amountFirstTx - gasFeesFirstTx,
4,
);
await terminateServiceWorker(driver);
await assertETHBalance(driver, totalAfterFirstTx);
// first send ETH #2 and then terminate SW
const amountSecondTx = roundToXDecimalPlaces(
generateRandNumBetween(0.5, 2),
4,
);
const gasFeesSecondTx = await simpleSendETH(
driver,
amountSecondTx,
RECIPIENT_ADDRESS,
);
const totalAfterSecondTx = roundToXDecimalPlaces(
initialBalance -
amountFirstTx -
gasFeesFirstTx -
amountSecondTx -
gasFeesSecondTx,
4,
);
await terminateServiceWorker(driver);
await assertETHBalance(driver, totalAfterSecondTx);
// first terminate SW and then send ETH
const amountThirdTx = roundToXDecimalPlaces(
generateRandNumBetween(0.5, 2),
4,
);
const gasFeesThirdTx = await simpleSendETH(
driver,
amountThirdTx,
RECIPIENT_ADDRESS,
);
const totalAfterThirdTx = roundToXDecimalPlaces(
initialBalance -
amountFirstTx -
gasFeesFirstTx -
amountSecondTx -
gasFeesSecondTx -
amountThirdTx -
gasFeesThirdTx,
4,
);
await assertETHBalance(driver, totalAfterThirdTx);
},
);
async function simpleSendETH(driver, value, recipient) {
await switchToWindow(driver, WINDOW_TITLES.ExtensionInFullScreenView);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill('[data-testid="ens-input"]', recipient);
const formattedValue = `${value}`.replace('.', ',');
await driver.fill('.unit-input__input', formattedValue);
await driver.clickElement('[data-testid="page-container-footer-next"]');
const gasFeesEl = await driver.findElement(
'.transaction-detail-item__detail-values .currency-display-component',
);
const gasFees = await gasFeesEl.getText();
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.clickElement('[data-testid="home__activity-tab"]');
await driver.findElement('.transaction-list-item');
// reset view to assets tab
await driver.clickElement('[data-testid="home__asset-tab"]');
return gasFees;
}
async function assertETHBalance(driver, expectedBalance) {
await switchToWindow(driver, WINDOW_TITLES.ExtensionInFullScreenView);
const isETHBalanceOverviewPresentAndVisible =
await driver.isElementPresentAndVisible({
css: '[data-testid="eth-overview__primary-currency"]',
text: `${expectedBalance} ETH`,
});
assert.equal(
isETHBalanceOverviewPresentAndVisible,
true,
`Balance DOM element should be visible and match ${expectedBalance} ETH.`,
);
}
});
it('Should continue to support add network dApp interactions after service worker re-starts multiple times', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions: generateGanacheOptions({
concurrent: { port: 8546, chainId: 1338 },
}),
title: this.test.title,
driverOptions: { openDevToolsForTabs: true },
},
async ({ driver }) => {
await driver.navigate();
await unlockWallet(driver, WALLET_PASSWORD);
await openDapp(driver);
// Click add Ethereum chain
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await driver.clickElement('#addEthereumChain');
await driver.waitUntilXWindowHandles(2);
// Notification pop up opens
await switchToWindow(driver, WINDOW_TITLES.Notification);
let notification = await driver.isElementPresent({
text: 'Allow this site to add a network?',
tag: 'h3',
});
assert.ok(notification, 'Dapp action does not appear in Metamask');
// Cancel Notification
await driver.clickElement({ text: 'Cancel', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
// Terminate Service Worker
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await terminateServiceWorker(driver);
// Click add Ethereum chain #2
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await driver.clickElement('#addEthereumChain');
await driver.waitUntilXWindowHandles(2);
// Notification pop up opens
await switchToWindow(driver, WINDOW_TITLES.Notification);
notification = await driver.isElementPresent({
text: 'Allow this site to add a network?',
tag: 'h3',
});
assert.ok(notification, 'Dapp action does not appear in Metamask');
// Cancel Notification
await driver.clickElement({ text: 'Cancel', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
// Terminate Service Worker
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await terminateServiceWorker(driver);
// Click add Ethereum chain #3
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await driver.clickElement('#addEthereumChain');
await driver.waitUntilXWindowHandles(2);
// Notification pop up opens
await switchToWindow(driver, WINDOW_TITLES.Notification);
notification = await driver.isElementPresent({
text: 'Allow this site to add a network?',
tag: 'h3',
});
assert.ok(notification, 'Dapp action does not appear in Metamask');
// Accept Notification
await driver.clickElement({ text: 'Approve', tag: 'button' });
await driver.clickElement({ text: 'Switch network', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
},
);
});
it('Should continue to support send ETH dApp interactions after service worker re-starts multiple times', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions: generateGanacheOptions({
concurrent: { port: 8546, chainId: 1338 },
}),
title: this.test.title,
driverOptions: { openDevToolsForTabs: true },
},
async ({ driver }) => {
await driver.navigate();
await unlockWallet(driver, WALLET_PASSWORD);
await openDapp(driver);
await clickSendButton(driver);
await driver.waitUntilXWindowHandles(2);
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await terminateServiceWorker(driver);
await driver.waitUntilXWindowHandles(2);
await clickSendButton(driver);
await driver.waitUntilXWindowHandles(2);
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await terminateServiceWorker(driver);
await clickSendButton(driver);
await driver.waitUntilXWindowHandles(2);
await assertNumberOfTransactionsInPopUp(driver, 3);
await confirmETHSendNotification(driver, 1);
await assertNumberOfTransactionsInPopUp(driver, 2);
await confirmETHSendNotification(driver, 1);
await confirmETHSendNotification(driver, 1);
},
);
async function clickSendButton(driver) {
// Click send button
await switchToWindow(driver, WINDOW_TITLES.TestDApp);
await driver.waitForSelector({
css: '#sendButton',
text: 'Send',
});
await driver.clickElement('#sendButton');
}
async function confirmETHSendNotification(driver, amount) {
await switchToWindow(driver, WINDOW_TITLES.Notification);
await driver.clickElement({
text: 'Edit',
tag: 'span',
});
await driver.fill('[data-testid="currency-input"]', amount);
await driver.clickElement({
text: 'Next',
tag: 'button',
});
await driver.clickElement({
text: 'Confirm',
tag: 'button',
});
}
async function assertNumberOfTransactionsInPopUp(driver, number) {
await switchToWindow(driver, WINDOW_TITLES.Notification);
const navEl = await driver.findElement(
'.confirm-page-container-navigation__navtext',
);
const notificationProgress = await navEl.getText();
assert.ok(notificationProgress, `1 of ${number}`);
}
});
it('Should lock wallet when a browser session ends (after turning off the extension)', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions: generateGanacheOptions({
concurrent: { port: 8546, chainId: 1338 },
}),
title: this.test.title,
},
async ({ driver }) => {
const { extensionUrl } = driver;
const extensionId = extensionUrl.split('//')[1];
await driver.navigate();
await unlockWallet(driver, WALLET_PASSWORD);
await reloadExtension(driver, extensionId);
// ensure extension finishes reloading before reopening full screen extension
await sleepSeconds(0.1);
await driver.openNewPage(`${extensionUrl}/home.html`);
const passwordField = await driver.isElementPresent('#password');
assert.ok(
passwordField,
'Password screen is not visible. Wallet should have been locked.',
);
},
);
async function reloadExtension(driver, extensionId) {
await switchToWindow(driver, WINDOW_TITLES.ExtensionInFullScreenView);
await driver.openNewPage('chrome://extensions/');
// extensions-manager
const extensionsManager = await driver.findElement('extensions-manager');
// shadowRoot
const extensionsManagerShadowRoot = await driver.executeScript(
'return arguments[0][0].shadowRoot',
extensionsManager,
);
// cr-view-manager
const viewManager = await extensionsManagerShadowRoot.findElement({
css: '#viewManager',
});
// extensions-item-list
const itemList = await viewManager.findElement({
css: '#items-list',
});
// shadowRoot
const itemListShadowRoot = await driver.executeScript(
'return arguments[0][0].shadowRoot',
itemList,
);
// extension-item
const extensionItem = await await itemListShadowRoot.findElement({
css: `#${extensionId}`,
});
// shadowRoot
const extensionItemShadowRoot = await driver.executeScript(
'return arguments[0][0].shadowRoot',
extensionItem,
);
// cr-icon-button
const devReloadButton = await extensionItemShadowRoot.findElement({
css: '#dev-reload-button',
});
// shadowRoot
const devReloadButtonShadowRoot = await driver.executeScript(
'return arguments[0][0].shadowRoot',
devReloadButton,
);
// cr-icon-button
const reloadBtn = await devReloadButtonShadowRoot.findElement({
css: '#maskedImage',
});
await reloadBtn.click();
}
});
});

View File

@ -5,9 +5,11 @@ const {
openDapp,
defaultGanacheOptions,
assertAccountBalanceForDOM,
restartServiceWorker,
SERVICE_WORKER_URL,
regularDelayMs,
WALLET_PASSWORD,
unlockWallet,
terminateServiceWorker,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
@ -28,12 +30,12 @@ describe('Phishing warning page', function () {
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
// log in wallet
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await unlockWallet(driver, WALLET_PASSWORD);
// DAPP is detected as phishing page
await openDapp(driver);
const phishingPageHeader = await driver.findElements({
text: 'Deceptive site ahead',
tag: 'h1',
@ -42,7 +44,7 @@ describe('Phishing warning page', function () {
// Restart service worker
await driver.openNewPage(SERVICE_WORKER_URL);
await restartServiceWorker(driver);
await terminateServiceWorker(driver);
await driver.delay(regularDelayMs);
// wait until extension is reloaded
@ -55,6 +57,7 @@ describe('Phishing warning page', function () {
await openDapp(driver);
// - extension, dapp, service worker and new dapp
await driver.waitUntilXWindowHandles(4);
const newPhishingPageHeader = await driver.findElements({
text: 'Deceptive site ahead',
tag: 'h1',

View File

@ -3,8 +3,8 @@ const {
withFixtures,
openDapp,
DAPP_URL,
login,
defaultGanacheOptions,
unlockWallet,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
@ -21,7 +21,7 @@ describe('Eth sign', function () {
},
async ({ driver }) => {
await driver.navigate();
await login(driver);
await unlockWallet(driver);
await openDapp(driver);
await driver.clickElement('#ethSign');
@ -56,7 +56,7 @@ describe('Eth sign', function () {
},
async ({ driver }) => {
await driver.navigate();
await login(driver);
await unlockWallet(driver);
await openDapp(driver);
await driver.clickElement('#ethSign');

View File

@ -407,6 +407,11 @@ class Driver {
await this.driver.close();
}
async closeWindowHandle(windowHandle) {
await this.driver.switchTo().window(windowHandle);
await this.driver.close();
}
// Close Alert Popup
async closeAlertPopup() {
return await this.driver.switchTo().alert().accept();

View File

@ -10170,13 +10170,13 @@ __metadata:
linkType: hard
"axios@npm:^1.2.1":
version: 1.2.2
resolution: "axios@npm:1.2.2"
version: 1.4.0
resolution: "axios@npm:1.4.0"
dependencies:
follow-redirects: ^1.15.0
form-data: ^4.0.0
proxy-from-env: ^1.1.0
checksum: 6e357491b38426c5720f7328ecbafca3c643b03952c052d787570672ce7a9365717c2d64db4ce97cfbee3f830fa405101e360e14d0857ef7f96a9f4d814c4e03
checksum: 7fb6a4313bae7f45e89d62c70a800913c303df653f19eafec88e56cea2e3821066b8409bc68be1930ecca80e861c52aa787659df0ffec6ad4d451c7816b9386b
languageName: node
linkType: hard
@ -12570,9 +12570,9 @@ __metadata:
linkType: hard
"compare-versions@npm:^5.0.1":
version: 5.0.1
resolution: "compare-versions@npm:5.0.1"
checksum: 302a4e46224b47b9280cf894c6c87d8df912671fa391dcdbf0e63438d9b0a69fe20dd747fb439e8d54c43af016ff4eaaf0a4c9d8e7ca358bcd12dadf4ad2935e
version: 5.0.3
resolution: "compare-versions@npm:5.0.3"
checksum: f66a4bb6ef8ff32031cc92c04dea4bbead039e72a7f6c7df7ef05f5a42ddca9202f8875b7449add54181e73b89f039662a8760c8db0ab036c4e8f653a7cd29c1
languageName: node
linkType: hard