From 4ecc8bd73550dd5da9d2f4e874abe25ee2788a18 Mon Sep 17 00:00:00 2001 From: Maxim Geerinck Date: Tue, 7 Feb 2023 10:49:32 +0100 Subject: [PATCH 1/5] wip --- .gitignore | 4 + commands/phantom.js | 1278 +++++++++++++++++++++++++++++++++++++++++ commands/synthetix.js | 2 +- helpers.js | 2 +- plugins/index.js | 6 +- synpress.js | 0 6 files changed, 1288 insertions(+), 4 deletions(-) create mode 100644 commands/phantom.js mode change 100644 => 100755 synpress.js diff --git a/.gitignore b/.gitignore index 0835a21..81f3bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ docker/screenshots tests/e2e/videos tests/e2e/videos-ci tests/e2e/screenshots + +dist/ +.DS_Store +*.zip \ No newline at end of file diff --git a/commands/phantom.js b/commands/phantom.js new file mode 100644 index 0000000..017d003 --- /dev/null +++ b/commands/phantom.js @@ -0,0 +1,1278 @@ +const log = require('debug')('synpress:metamask'); +const playwright = require('./playwright'); + +const { + welcomePageElements, + firstTimeFlowPageElements, + metametricsPageElements, + firstTimeFlowImportPageElements, + firstTimeFlowCreatePagePageElements, + secureYourWalletPageElements, + revealSeedPageElements, + endOfFlowPageElements, +} = require('../pages/metamask/first-time-flow-page'); +const { mainPageElements } = require('../pages/metamask/main-page'); +const { unlockPageElements } = require('../pages/metamask/unlock-page'); +const { + notificationPageElements, + permissionsPageElements, + confirmPageElements, + signaturePageElements, + encryptionPublicKeyPageElements, + decryptPageElements, + dataSignaturePageElements, + recipientPopupElements, + addTokenPageElements, +} = require('../pages/metamask/notification-page'); +const { + settingsPageElements, + advancedPageElements, + experimentalSettingsPageElements, + resetAccountModalElements, + addNetworkPageElements, +} = require('../pages/metamask/settings-page'); +const { + confirmationPageElements, +} = require('../pages/metamask/confirmation-page'); +const { setNetwork } = require('../helpers'); + +let extensionInitialUrl; +let extensionId; +let extensionHomeUrl; +let extensionSettingsUrl; +let extensionAdvancedSettingsUrl; +let extensionExperimentalSettingsUrl; +let extensionAddNetworkUrl; +let extensionNewAccountUrl; +let extensionImportAccountUrl; +let extensionImportTokenUrl; +let walletAddress; +let switchBackToCypressWindow; + +module.exports = { + extensionId: () => { + return extensionId; + }, + extensionUrls: () => { + return { + extensionInitialUrl, + extensionHomeUrl, + extensionSettingsUrl, + extensionAdvancedSettingsUrl, + extensionExperimentalSettingsUrl, + extensionAddNetworkUrl, + extensionNewAccountUrl, + extensionImportAccountUrl, + extensionImportTokenUrl, + }; + }, + walletAddress: () => { + return walletAddress; + }, + goTo: async url => { + await Promise.all([ + playwright.metamaskWindow().waitForNavigation(), + playwright.metamaskWindow().goto(url), + ]); + await playwright.waitUntilStable(); + }, + goToHome: async () => { + await module.exports.goTo(extensionHomeUrl); + }, + goToSettings: async () => { + await module.exports.goTo(extensionSettingsUrl); + }, + goToAdvancedSettings: async () => { + await module.exports.goTo(extensionAdvancedSettingsUrl); + }, + goToExperimentalSettings: async () => { + await module.exports.goTo(extensionExperimentalSettingsUrl); + }, + goToAddNetwork: async () => { + await module.exports.goTo(extensionAddNetworkUrl); + }, + goToNewAccount: async () => { + await module.exports.goTo(extensionNewAccountUrl); + }, + goToImportAccount: async () => { + await module.exports.goTo(extensionImportAccountUrl); + }, + goToImportToken: async () => { + await module.exports.goTo(extensionImportTokenUrl); + }, + getExtensionDetails: async () => { + extensionInitialUrl = await playwright.metamaskWindow().url(); + extensionId = extensionInitialUrl.match('//(.*?)/')[1]; + extensionHomeUrl = `chrome-extension://${extensionId}/home.html`; + extensionSettingsUrl = `${extensionHomeUrl}#settings`; + extensionAdvancedSettingsUrl = `${extensionSettingsUrl}/advanced`; + extensionExperimentalSettingsUrl = `${extensionSettingsUrl}/experimental`; + extensionAddNetworkUrl = `${extensionSettingsUrl}/networks/add-network`; + extensionNewAccountUrl = `${extensionHomeUrl}#new-account`; + extensionImportAccountUrl = `${extensionNewAccountUrl}/import`; + extensionImportTokenUrl = `${extensionHomeUrl}#import-token`; + + return { + extensionInitialUrl, + extensionId, + extensionSettingsUrl, + extensionAdvancedSettingsUrl, + extensionExperimentalSettingsUrl, + extensionAddNetworkUrl, + extensionNewAccountUrl, + extensionImportAccountUrl, + extensionImportTokenUrl, + }; + }, + // workaround for metamask random blank page on first run + fixBlankPage: async () => { + await playwright.metamaskWindow().waitForTimeout(1000); + for (let times = 0; times < 5; times++) { + if ( + !(await playwright + .metamaskWindow() + .locator(welcomePageElements.app) + .isVisible()) + ) { + await playwright.metamaskWindow().reload(); + await playwright.metamaskWindow().waitForTimeout(2000); + } else { + break; + } + } + }, + confirmWelcomePage: async () => { + await module.exports.fixBlankPage(); + await playwright.waitAndClick( + welcomePageElements.confirmButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + return true; + }, + closePopupAndTooltips: async () => { + // note: this is required for fast execution of e2e tests to avoid flakiness + // otherwise popup may not be detected properly and not closed + await playwright.metamaskWindow().waitForTimeout(1000); + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.popup.container) + .isVisible() + ) { + const popupBackground = playwright + .metamaskWindow() + .locator(mainPageElements.popup.background); + const popupBackgroundBox = await popupBackground.boundingBox(); + await playwright + .metamaskWindow() + .mouse.click(popupBackgroundBox.x + 1, popupBackgroundBox.y + 1); + } + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.tippyTooltip.closeButton) + .isVisible() + ) { + await playwright.waitAndClick(mainPageElements.tippyTooltip.closeButton); + } + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.actionableMessage.closeButton) + .isVisible() + ) { + await playwright.waitAndClick( + mainPageElements.actionableMessage.closeButton, + ); + } + return true; + }, + closeModal: async () => { + // note: this is required for fast execution of e2e tests to avoid flakiness + // otherwise modal may not be detected properly and not closed + await playwright.metamaskWindow().waitForTimeout(1000); + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.connectedSites.modal) + .isVisible() + ) { + await playwright.waitAndClick( + mainPageElements.connectedSites.closeButton, + ); + } + return true; + }, + unlock: async password => { + await module.exports.fixBlankPage(); + await playwright.waitAndType(unlockPageElements.passwordInput, password); + await playwright.waitAndClick( + unlockPageElements.unlockButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + return true; + }, + optOutAnalytics: async () => { + await playwright.waitAndClick( + metametricsPageElements.optOutAnalyticsButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + return true; + }, + importWallet: async (secretWords, password) => { + await module.exports.optOutAnalytics(); + await playwright.waitAndClick( + firstTimeFlowPageElements.importWalletButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + // todo: add support for more secret words (15/18/21/24) + for (const [index, word] of secretWords.split(' ').entries()) { + await playwright.waitAndType( + firstTimeFlowImportPageElements.secretWordsInput(index), + word, + ); + } + await playwright.waitAndType( + firstTimeFlowImportPageElements.passwordInput, + password, + ); + await playwright.waitAndType( + firstTimeFlowImportPageElements.confirmPasswordInput, + password, + ); + await playwright.waitAndClick( + firstTimeFlowImportPageElements.termsCheckbox, + ); + await playwright.waitAndClick( + firstTimeFlowImportPageElements.importButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await playwright.waitAndClick( + endOfFlowPageElements.allDoneButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + return true; + }, + createWallet: async password => { + await module.exports.optOutAnalytics(); + await playwright.waitAndClick( + firstTimeFlowPageElements.createWalletButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await playwright.waitAndType( + firstTimeFlowCreatePagePageElements.newPasswordInput, + password, + ); + await playwright.waitAndType( + firstTimeFlowCreatePagePageElements.confirmNewPasswordInput, + password, + ); + await playwright.waitAndClick( + firstTimeFlowCreatePagePageElements.newSignupCheckbox, + ); + await playwright.waitAndClick( + firstTimeFlowCreatePagePageElements.createButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await playwright.waitAndClick( + secureYourWalletPageElements.nextButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await playwright.waitAndClick( + revealSeedPageElements.remindLaterButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + return true; + }, + importAccount: async privateKey => { + await switchToMetamaskIfNotActive(); + await module.exports.goToImportAccount(); + await playwright.waitAndType( + mainPageElements.importAccount.input, + privateKey, + ); + await playwright.waitAndClick( + mainPageElements.importAccount.importButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + await switchToCypressIfNotActive(); + return true; + }, + createAccount: async accountName => { + if (accountName) { + accountName = accountName.toLowerCase(); + } + await switchToMetamaskIfNotActive(); + await module.exports.goToNewAccount(); + if (accountName) { + await playwright.waitAndType( + mainPageElements.createAccount.input, + accountName, + ); + } + await playwright.waitAndClick(mainPageElements.createAccount.createButton); + await module.exports.closePopupAndTooltips(); + await switchToCypressIfNotActive(); + return true; + }, + switchAccount: async accountNameOrAccountNumber => { + if (typeof accountNameOrAccountNumber === 'string') { + accountNameOrAccountNumber = accountNameOrAccountNumber.toLowerCase(); + } + await switchToMetamaskIfNotActive(); + // note: closePopupAndTooltips() is required after changing createAccount() to use direct urls (popup started appearing) + // ^ this change also introduced 500ms delay for closePopupAndTooltips() function + await module.exports.closePopupAndTooltips(); + await playwright.waitAndClick(mainPageElements.accountMenu.button); + if (typeof accountNameOrAccountNumber === 'number') { + await playwright.waitAndClick( + mainPageElements.accountMenu.accountButton(accountNameOrAccountNumber), + ); + } else { + await playwright.waitAndClickByText( + mainPageElements.accountMenu.accountName, + accountNameOrAccountNumber, + ); + } + await module.exports.closePopupAndTooltips(); + await switchToCypressIfNotActive(); + return true; + }, + changeNetwork: async network => { + await switchToMetamaskIfNotActive(); + await playwright.waitAndClick(mainPageElements.networkSwitcher.button); + if (typeof network === 'string') { + network = network.toLowerCase(); + if (network === 'mainnet') { + await playwright.waitAndClick( + mainPageElements.networkSwitcher.mainnetNetworkItem, + ); + } else if (network === 'goerli') { + await playwright.waitAndClick( + mainPageElements.networkSwitcher.goerliNetworkItem, + ); + } else if (network === 'sepolia') { + await playwright.waitAndClick( + mainPageElements.networkSwitcher.sepoliaNetworkItem, + ); + } else if (network === 'localhost') { + await playwright.waitAndClick( + mainPageElements.networkSwitcher.localhostNetworkItem, + ); + } else { + await playwright.waitAndClickByText( + mainPageElements.networkSwitcher.dropdownMenuItem, + network, + ); + } + await playwright.waitForText( + mainPageElements.networkSwitcher.networkName, + network, + ); + } else if (typeof network === 'object') { + network.networkName = network.networkName.toLowerCase(); + await playwright.waitAndClickByText( + mainPageElements.networkSwitcher.dropdownMenuItem, + network.networkName, + ); + await playwright.waitForText( + mainPageElements.networkSwitcher.networkName, + network.networkName, + ); + } + await module.exports.closePopupAndTooltips(); + await setNetwork(network); + await switchToCypressIfNotActive(); + return true; + }, + addNetwork: async network => { + await switchToMetamaskIfNotActive(); + if ( + process.env.NETWORK_NAME && + process.env.RPC_URL && + process.env.CHAIN_ID + ) { + network = { + networkName: process.env.NETWORK_NAME, + rpcUrl: process.env.RPC_URL, + chainId: process.env.CHAIN_ID, + symbol: process.env.SYMBOL, + blockExplorer: process.env.BLOCK_EXPLORER, + isTestnet: process.env.IS_TESTNET, + }; + } + if (typeof network === 'string') { + network = network.toLowerCase(); + } else if (typeof network === 'object') { + network.networkName = network.networkName.toLowerCase(); + } + await module.exports.goToAddNetwork(); + await playwright.waitAndType( + addNetworkPageElements.networkNameInput, + network.networkName, + ); + await playwright.waitAndType( + addNetworkPageElements.rpcUrlInput, + network.rpcUrl, + ); + await playwright.waitAndType( + addNetworkPageElements.chainIdInput, + network.chainId, + ); + if (network.symbol) { + await playwright.waitAndType( + addNetworkPageElements.symbolInput, + network.symbol, + ); + } + if (network.blockExplorer) { + await playwright.waitAndType( + addNetworkPageElements.blockExplorerInput, + network.blockExplorer, + ); + } + await playwright.waitAndClick( + addNetworkPageElements.saveButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + await setNetwork(network); + await playwright.waitForText( + mainPageElements.networkSwitcher.networkName, + network.networkName, + ); + await switchToCypressIfNotActive(); + return true; + }, + async disconnectWalletFromDapp() { + await switchToMetamaskIfNotActive(); + await playwright.waitAndClick(mainPageElements.optionsMenu.button); + await playwright.waitAndClick( + mainPageElements.optionsMenu.connectedSitesButton, + ); + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.connectedSites.disconnectLabel) + .isVisible() + ) { + console.log( + '[disconnectWalletFromDapp] Wallet is connected to a dapp, disconnecting..', + ); + await playwright.waitAndClick( + mainPageElements.connectedSites.disconnectLabel, + ); + await playwright.waitAndClick( + mainPageElements.connectedSites.disconnectButton, + ); + } else { + console.log( + '[disconnectWalletFromDapp] Wallet is not connected to a dapp, skipping..', + ); + } + await module.exports.closeModal(); + await switchToCypressIfNotActive(); + return true; + }, + async disconnectWalletFromAllDapps() { + await switchToMetamaskIfNotActive(); + await playwright.waitAndClick(mainPageElements.optionsMenu.button); + await playwright.waitAndClick( + mainPageElements.optionsMenu.connectedSitesButton, + ); + const disconnectLabels = await playwright + .metamaskWindow() + .$$(mainPageElements.connectedSites.disconnectLabel); + if (disconnectLabels.length) { + console.log( + '[disconnectWalletFromAllDapps] Wallet is connected to dapps, disconnecting..', + ); + // eslint-disable-next-line no-unused-vars + for (const disconnectLabel of disconnectLabels) { + await playwright.waitAndClick( + mainPageElements.connectedSites.disconnectLabel, + ); + await playwright.waitAndClick( + mainPageElements.connectedSites.disconnectButton, + ); + } + } else { + console.log( + '[disconnectWalletFromAllDapps] Wallet is not connected to any dapps, skipping..', + ); + } + await module.exports.closeModal(); + await switchToCypressIfNotActive(); + return true; + }, + activateAdvancedGasControl: async skipSetup => { + return await activateAdvancedSetting( + advancedPageElements.advancedGasControlToggleOn, + advancedPageElements.advancedGasControlToggleOff, + skipSetup, + ); + }, + activateEnhancedTokenDetection: async skipSetup => { + return await activateAdvancedSetting( + advancedPageElements.enhancedTokenDetectionToggleOn, + advancedPageElements.enhancedTokenDetectionToggleOff, + skipSetup, + ); + }, + activateShowHexData: async skipSetup => { + return await activateAdvancedSetting( + advancedPageElements.showHexDataToggleOn, + advancedPageElements.showHexDataToggleOff, + skipSetup, + ); + }, + activateTestnetConversion: async skipSetup => { + return await activateAdvancedSetting( + advancedPageElements.showTestnetConversionOn, + advancedPageElements.showTestnetConversionOff, + skipSetup, + ); + }, + activateShowTestnetNetworks: async skipSetup => { + return await activateAdvancedSetting( + advancedPageElements.showTestnetNetworksOn, + advancedPageElements.showTestnetNetworksOff, + skipSetup, + ); + }, + activateCustomNonce: async skipSetup => { + return await activateAdvancedSetting( + advancedPageElements.customNonceToggleOn, + advancedPageElements.customNonceToggleOff, + skipSetup, + ); + }, + activateDismissBackupReminder: async skipSetup => { + return await activateAdvancedSetting( + advancedPageElements.dismissBackupReminderOn, + advancedPageElements.dismissBackupReminderOff, + skipSetup, + ); + }, + activateEnhancedGasFeeUI: async skipSetup => { + return await activateAdvancedSetting( + experimentalSettingsPageElements.enhancedGasFeeUIToggleOn, + experimentalSettingsPageElements.enhancedGasFeeUIToggleOff, + skipSetup, + true, + ); + }, + activateShowCustomNetworkList: async skipSetup => { + return await activateAdvancedSetting( + experimentalSettingsPageElements.showCustomNetworkListToggleOn, + experimentalSettingsPageElements.showCustomNetworkListToggleOff, + skipSetup, + true, + ); + }, + resetAccount: async () => { + await switchToMetamaskIfNotActive(); + await module.exports.goToAdvancedSettings(); + await playwright.waitAndClick(advancedPageElements.resetAccountButton); + await playwright.waitAndClick(resetAccountModalElements.resetButton); + await playwright.waitAndClick( + settingsPageElements.closeButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + await switchToCypressIfNotActive(); + return true; + }, + confirmSignatureRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + if ( + await playwright + .metamaskNotificationWindow() + .locator(signaturePageElements.signatureRequestScrollDownButton) + .isVisible() + ) { + await playwright.waitAndClick( + signaturePageElements.signatureRequestScrollDownButton, + notificationPage, + ); + } + await playwright.waitAndClick( + signaturePageElements.confirmSignatureRequestButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectSignatureRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + signaturePageElements.rejectSignatureRequestButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + confirmDataSignatureRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + if ( + await playwright + .metamaskNotificationWindow() + .locator(signaturePageElements.signatureRequestScrollDownButton) + .isVisible() + ) { + await playwright.waitAndClick( + signaturePageElements.signatureRequestScrollDownButton, + notificationPage, + ); + } + await playwright.waitAndClick( + dataSignaturePageElements.confirmDataSignatureRequestButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectDataSignatureRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + dataSignaturePageElements.rejectDataSignatureRequestButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + importToken: async tokenConfig => { + let tokenData = {}; + await switchToMetamaskIfNotActive(); + await module.exports.goToImportToken(); + if (typeof tokenConfig === 'string') { + await playwright.waitAndType( + mainPageElements.importToken.tokenContractAddressInput, + tokenConfig, + ); + tokenData.tokenContractAddress = tokenConfig; + tokenData.tokenSymbol = await playwright.waitAndGetInputValue( + mainPageElements.importToken.tokenSymbolInput, + ); + } else { + await playwright.waitAndType( + mainPageElements.importToken.tokenContractAddressInput, + tokenConfig.address, + ); + tokenData.tokenContractAddress = tokenConfig.address; + await playwright.waitAndClick( + mainPageElements.importToken.tokenEditButton, + await playwright.metamaskWindow(), + { + force: true, + }, + ); + await playwright.waitClearAndType( + tokenConfig.symbol, + mainPageElements.importToken.tokenSymbolInput, + ); + tokenData.tokenSymbol = tokenConfig.symbol; + } + tokenData.tokenDecimals = await playwright.waitAndGetInputValue( + mainPageElements.importToken.tokenDecimalInput, + ); + await playwright.waitAndClick( + mainPageElements.importToken.addCustomTokenButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await playwright.waitAndClick( + mainPageElements.importToken.importTokensButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await playwright.waitAndClick( + mainPageElements.asset.backButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + await switchToCypressIfNotActive(); + tokenData.imported = true; + return tokenData; + }, + confirmAddToken: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + addTokenPageElements.confirmAddTokenButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectAddToken: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + addTokenPageElements.rejectAddTokenButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + confirmPermissionToSpend: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + notificationPageElements.allowToSpendButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectPermissionToSpend: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + notificationPageElements.rejectToSpendButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + acceptAccess: async options => { + const notificationPage = await playwright.switchToMetamaskNotification(); + if (options && options.allAccounts) { + await playwright.waitAndClick( + notificationPageElements.selectAllCheckbox, + notificationPage, + ); + } + await playwright.waitAndClick( + notificationPageElements.nextButton, + notificationPage, + { waitForEvent: 'navi' }, + ); + if (options && options.signInSignature) { + await playwright.waitAndClick( + permissionsPageElements.connectButton, + notificationPage, + { waitForEvent: 'navi' }, + ); + await module.exports.confirmSignatureRequest(); + } else { + await playwright.waitAndClick( + permissionsPageElements.connectButton, + notificationPage, + { waitForEvent: 'close' }, + ); + } + return true; + }, + confirmTransaction: async gasConfig => { + let txData = {}; + const notificationPage = await playwright.switchToMetamaskNotification(); + if (gasConfig) { + log( + '[confirmTransaction] gasConfig is present, determining transaction type..', + ); + if ( + await playwright + .metamaskNotificationWindow() + .locator(confirmPageElements.editGasFeeLegacyButton) + .isVisible() + ) { + log('[confirmTransaction] Looks like legacy tx'); + if (typeof gasConfig === 'object') { + log('[confirmTransaction] Editing legacy tx..'); + await playwright.waitAndClick( + confirmPageElements.editGasFeeLegacyButton, + notificationPage, + ); + if ( + await playwright + .metamaskNotificationWindow() + .locator(confirmPageElements.editGasFeeLegacyOverrideAckButton) + .isVisible() + ) { + log( + '[confirmTransaction] Override acknowledgement modal is present, closing..', + ); + await playwright.waitAndClick( + confirmPageElements.editGasFeeLegacyOverrideAckButton, + notificationPage, + ); + } + if (gasConfig.gasLimit) { + log('[confirmTransaction] Changing gas limit..'); + await playwright.waitAndSetValue( + gasConfig.gasLimit.toString(), + confirmPageElements.gasLimitLegacyInput, + notificationPage, + ); + } + if (gasConfig.gasPrice) { + log('[confirmTransaction] Changing gas price..'); + await playwright.waitAndSetValue( + gasConfig.gasPrice.toString(), + confirmPageElements.gasPriceLegacyInput, + notificationPage, + ); + } + await playwright.waitAndClick( + confirmPageElements.saveCustomGasFeeButton, + notificationPage, + ); + } else { + log( + "[confirmTransaction] Legacy tx doesn't support eip-1559 fees (low, market, aggressive, site), using default values..", + ); + } + } else { + log('[confirmTransaction] Looks like eip-1559 tx'); + await playwright.waitAndClick( + confirmPageElements.editGasFeeButton, + notificationPage, + ); + if (typeof gasConfig === 'string') { + if (gasConfig === 'low') { + log('[confirmTransaction] Changing gas fee to low..'); + await playwright.waitAndClick( + confirmPageElements.gasOptionLowButton, + notificationPage, + ); + } else if (gasConfig === 'market') { + log('[confirmTransaction] Changing gas fee to market..'); + await playwright.waitAndClick( + confirmPageElements.gasOptionMediumButton, + notificationPage, + ); + } else if (gasConfig === 'aggressive') { + log('[confirmTransaction] Changing gas fee to aggressive..'); + await playwright.waitAndClick( + confirmPageElements.gasOptionHighButton, + notificationPage, + ); + } else if (gasConfig === 'site') { + log('[confirmTransaction] Changing gas fee to site suggested..'); + await playwright.waitAndClick( + confirmPageElements.gasOptionDappSuggestedButton, + notificationPage, + ); + } + } else { + log('[confirmTransaction] Editing eip-1559 tx..'); + await playwright.waitAndClick( + confirmPageElements.gasOptionCustomButton, + notificationPage, + ); + if (gasConfig.gasLimit) { + log('[confirmTransaction] Changing gas limit..'); + await playwright.waitAndClick( + confirmPageElements.editGasLimitButton, + notificationPage, + ); + await playwright.waitAndSetValue( + gasConfig.gasLimit.toString(), + confirmPageElements.gasLimitInput, + notificationPage, + ); + } + if (gasConfig.baseFee) { + log('[confirmTransaction] Changing base fee..'); + await playwright.waitAndSetValue( + gasConfig.baseFee.toString(), + confirmPageElements.baseFeeInput, + notificationPage, + ); + } + if (gasConfig.priorityFee) { + log('[confirmTransaction] Changing priority fee..'); + await playwright.waitAndSetValue( + gasConfig.priorityFee.toString(), + confirmPageElements.priorityFeeInput, + notificationPage, + ); + } + await playwright.waitAndClick( + confirmPageElements.saveCustomGasFeeButton, + notificationPage, + ); + } + } + } + log('[confirmTransaction] Checking if recipient address is present..'); + if ( + await playwright + .metamaskNotificationWindow() + .locator(confirmPageElements.recipientButton) + .isVisible() + ) { + log('[confirmTransaction] Getting recipient address..'); + await playwright.waitAndClick( + confirmPageElements.recipientButton, + notificationPage, + ); + txData.recipientPublicAddress = await playwright.waitAndGetValue( + recipientPopupElements.recipientPublicAddress, + notificationPage, + ); + await playwright.waitAndClick( + recipientPopupElements.popupCloseButton, + notificationPage, + ); + } + log('[confirmTransaction] Checking if network name is present..'); + if ( + await playwright + .metamaskNotificationWindow() + .locator(confirmPageElements.networkLabel) + .isVisible() + ) { + log('[confirmTransaction] Getting network name..'); + txData.networkName = await playwright.waitAndGetValue( + confirmPageElements.networkLabel, + notificationPage, + ); + } + // todo: handle setting of custom nonce here + log('[confirmTransaction] Getting transaction nonce..'); + txData.customNonce = await playwright.waitAndGetAttributeValue( + confirmPageElements.customNonceInput, + 'placeholder', + notificationPage, + ); + // todo: fix getting tx data on function multicall + // log('[confirmTransaction] Checking if tx data is present..'); + // if ( + // await playwright + // .metamaskNotificationWindow() + // .locator(confirmPageElements.dataButton) + // .isVisible() + // ) { + // log('[confirmTransaction] Fetching tx data..'); + // await playwright.waitAndClick( + // confirmPageElements.dataButton, + // notificationPage, + // ); + // log('[confirmTransaction] Getting origin value..'); + // txData.origin = await playwright.waitAndGetValue( + // confirmPageElements.originValue, + // notificationPage, + // ); + // log('[confirmTransaction] Getting bytes value..'); + // txData.bytes = await playwright.waitAndGetValue( + // confirmPageElements.bytesValue, + // notificationPage, + // ); + // log('[confirmTransaction] Getting hex data value..'); + // txData.hexData = await playwright.waitAndGetValue( + // confirmPageElements.hexDataValue, + // notificationPage, + // ); + // await playwright.waitAndClick( + // confirmPageElements.detailsButton, + // notificationPage, + // ); + // } + log('[confirmTransaction] Confirming transaction..'); + await playwright.waitAndClick( + confirmPageElements.confirmButton, + notificationPage, + { waitForEvent: 'close' }, + ); + txData.confirmed = true; + log('[confirmTransaction] Transaction confirmed!'); + return txData; + }, + rejectTransaction: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + confirmPageElements.rejectButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + confirmEncryptionPublicKeyRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + encryptionPublicKeyPageElements.confirmEncryptionPublicKeyButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectEncryptionPublicKeyRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + encryptionPublicKeyPageElements.rejectEncryptionPublicKeyButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + confirmDecryptionRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + decryptPageElements.confirmDecryptionRequestButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectDecryptionRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + decryptPageElements.rejectDecryptionRequestButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + allowToAddNetwork: async ({ waitForEvent } = {}) => { + const notificationPage = await playwright.switchToMetamaskNotification(); + if (waitForEvent) { + await playwright.waitAndClick( + confirmationPageElements.footer.approveButton, + notificationPage, + { waitForEvent }, + ); + } else { + await playwright.waitAndClick( + confirmationPageElements.footer.approveButton, + notificationPage, + ); + } + return true; + }, + rejectToAddNetwork: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + confirmationPageElements.footer.cancelButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + allowToSwitchNetwork: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + confirmationPageElements.footer.approveButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectToSwitchNetwork: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + confirmationPageElements.footer.cancelButton, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + allowToAddAndSwitchNetwork: async () => { + await module.exports.allowToAddNetwork(); + await module.exports.allowToSwitchNetwork(); + return true; + }, + getWalletAddress: async () => { + await switchToMetamaskIfNotActive(); + await playwright.waitAndClick(mainPageElements.optionsMenu.button); + await playwright.waitAndClick( + mainPageElements.optionsMenu.accountDetailsButton, + ); + walletAddress = await playwright.waitAndGetValue( + mainPageElements.accountModal.walletAddressInput, + ); + await playwright.waitAndClick(mainPageElements.accountModal.closeButton); + await switchToCypressIfNotActive(); + return walletAddress; + }, + initialSetup: async ({ + secretWordsOrPrivateKey, + network, + password, + enableAdvancedSettings, + }) => { + const isCustomNetwork = + (process.env.NETWORK_NAME && + process.env.RPC_URL && + process.env.CHAIN_ID) || + typeof network == 'object'; + + await playwright.init(); + await playwright.assignWindows(); + await playwright.assignActiveTabName('metamask'); + await module.exports.getExtensionDetails(); + await module.exports.fixBlankPage(); + if ( + await playwright + .metamaskWindow() + .locator(welcomePageElements.confirmButton) + .isVisible() + ) { + await module.exports.confirmWelcomePage(); + if (secretWordsOrPrivateKey.includes(' ')) { + // secret words + await module.exports.importWallet(secretWordsOrPrivateKey, password); + } else { + // private key + await module.exports.createWallet(password); + await module.exports.importAccount(secretWordsOrPrivateKey); + } + + await setupSettings(enableAdvancedSettings); + + if (isCustomNetwork) { + await module.exports.addNetwork(network); + } else { + await module.exports.changeNetwork(network); + } + walletAddress = await module.exports.getWalletAddress(); + await playwright.switchToCypressWindow(); + return true; + } else if ( + await playwright + .metamaskWindow() + .locator(unlockPageElements.passwordInput) + .isVisible() + ) { + await module.exports.unlock(password); + walletAddress = await module.exports.getWalletAddress(); + await playwright.switchToCypressWindow(); + return true; + } else { + if ( + (await playwright + .metamaskWindow() + .locator(mainPageElements.walletOverview) + .isVisible()) && + !process.env.RESET_METAMASK + ) { + await switchToMetamaskIfNotActive(); + walletAddress = await module.exports.getWalletAddress(); + await playwright.switchToCypressWindow(); + return true; + } else { + // todo: reset metamask state + } + } + }, +}; + +async function switchToMetamaskIfNotActive() { + if (await playwright.isCypressWindowActive()) { + await playwright.switchToMetamaskWindow(); + switchBackToCypressWindow = true; + } + return switchBackToCypressWindow; +} + +async function switchToCypressIfNotActive() { + if (switchBackToCypressWindow) { + await playwright.switchToCypressWindow(); + switchBackToCypressWindow = false; + } + return switchBackToCypressWindow; +} + +async function activateAdvancedSetting( + toggleOn, + toggleOff, + skipSetup, + experimental, +) { + if (!skipSetup) { + await switchToMetamaskIfNotActive(); + if (experimental) { + await module.exports.goToExperimentalSettings(); + } else { + await module.exports.goToAdvancedSettings(); + } + } + if (!(await playwright.metamaskWindow().locator(toggleOn).isVisible())) { + await playwright.waitAndClick(toggleOff); + } + if (!skipSetup) { + await playwright.waitAndClick( + settingsPageElements.closeButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + await switchToCypressIfNotActive(); + } + return true; +} + +async function setupSettings(enableAdvancedSettings) { + await switchToMetamaskIfNotActive(); + await module.exports.goToAdvancedSettings(); + await module.exports.activateAdvancedGasControl(true); + await module.exports.activateShowHexData(true); + await module.exports.activateShowTestnetNetworks(true); + await module.exports.activateCustomNonce(true); + await module.exports.activateDismissBackupReminder(true); + if (enableAdvancedSettings) { + await module.exports.activateEnhancedTokenDetection(true); + await module.exports.activateTestnetConversion(true); + } + await module.exports.goToExperimentalSettings(); + await module.exports.activateEnhancedGasFeeUI(true); + await playwright.waitAndClick( + settingsPageElements.closeButton, + await playwright.metamaskWindow(), + { + waitForEvent: 'navi', + }, + ); + await module.exports.closePopupAndTooltips(); + await switchToCypressIfNotActive(); + return true; +} diff --git a/commands/synthetix.js b/commands/synthetix.js index 8f2ce16..84ef1e9 100644 --- a/commands/synthetix.js +++ b/commands/synthetix.js @@ -1,7 +1,7 @@ const { SynthetixJs } = require('synthetix-js'); const { synthetix } = require('@synthetixio/js'); const { getNetwork } = require('../helpers'); -const metamask = require('./metamask'); +const metamask = require('./phantom'); const bytes32 = require('bytes32'); const sleep = require('util').promisify(setTimeout); diff --git a/helpers.js b/helpers.js index 558aff3..83a9eb6 100644 --- a/helpers.js +++ b/helpers.js @@ -50,7 +50,7 @@ module.exports = { }, getSynpressPath: () => { if (process.env.SYNPRESS_LOCAL_TEST) { - return '.'; + return './node_modules/@synthetixio/synpress'; } else { return path.dirname(require.resolve(packageJson.name)); } diff --git a/plugins/index.js b/plugins/index.js index 56f1dfc..5078985 100644 --- a/plugins/index.js +++ b/plugins/index.js @@ -1,6 +1,6 @@ const helpers = require('../helpers'); const playwright = require('../commands/playwright'); -const metamask = require('../commands/metamask'); +const metamask = require('../commands/phantom'); const synthetix = require('../commands/synthetix'); const etherscan = require('../commands/etherscan'); @@ -38,7 +38,9 @@ module.exports = (on, config) => { const metamaskPath = await helpers.prepareMetamask( process.env.METAMASK_VERSION || '10.21.0', ); - arguments_.extensions.push(metamaskPath); + arguments_.extensions.push( + metamaskPath.replace('metamask-chrome-10.21.0', 'phantom'), + ); } return arguments_; diff --git a/synpress.js b/synpress.js old mode 100644 new mode 100755 From 4cfdc7276b0a461efed4c4f51c9d5abbbad898da Mon Sep 17 00:00:00 2001 From: Maxim Geerinck Date: Wed, 8 Feb 2023 12:42:36 +0100 Subject: [PATCH 2/5] feat: Make sure onboarding a wallet works --- commands/phantom.js | 222 ++++++++++++++------------ commands/playwright.js | 12 ++ pages/phantom/confirmation-page.js | 12 ++ pages/phantom/first-time-flow-page.js | 85 ++++++++++ pages/phantom/main-page.js | 153 ++++++++++++++++++ pages/phantom/notification-page.js | 146 +++++++++++++++++ pages/phantom/page.js | 16 ++ pages/phantom/settings-page.js | 94 +++++++++++ pages/phantom/unlock-page.js | 9 ++ plugins/index.js | 14 ++ 10 files changed, 658 insertions(+), 105 deletions(-) create mode 100644 pages/phantom/confirmation-page.js create mode 100644 pages/phantom/first-time-flow-page.js create mode 100644 pages/phantom/main-page.js create mode 100644 pages/phantom/notification-page.js create mode 100644 pages/phantom/page.js create mode 100644 pages/phantom/settings-page.js create mode 100644 pages/phantom/unlock-page.js diff --git a/commands/phantom.js b/commands/phantom.js index 017d003..13e5792 100644 --- a/commands/phantom.js +++ b/commands/phantom.js @@ -1,4 +1,4 @@ -const log = require('debug')('synpress:metamask'); +const log = require('debug')('synpress:phantom'); const playwright = require('./playwright'); const { @@ -9,10 +9,9 @@ const { firstTimeFlowCreatePagePageElements, secureYourWalletPageElements, revealSeedPageElements, - endOfFlowPageElements, -} = require('../pages/metamask/first-time-flow-page'); -const { mainPageElements } = require('../pages/metamask/main-page'); -const { unlockPageElements } = require('../pages/metamask/unlock-page'); +} = require('../pages/phantom/first-time-flow-page'); +const { mainPageElements } = require('../pages/phantom/main-page'); +const { unlockPageElements } = require('../pages/phantom/unlock-page'); const { notificationPageElements, permissionsPageElements, @@ -23,17 +22,17 @@ const { dataSignaturePageElements, recipientPopupElements, addTokenPageElements, -} = require('../pages/metamask/notification-page'); +} = require('../pages/phantom/notification-page'); const { settingsPageElements, advancedPageElements, experimentalSettingsPageElements, resetAccountModalElements, addNetworkPageElements, -} = require('../pages/metamask/settings-page'); +} = require('../pages/phantom/settings-page'); const { confirmationPageElements, -} = require('../pages/metamask/confirmation-page'); +} = require('../pages/phantom/confirmation-page'); const { setNetwork } = require('../helpers'); let extensionInitialUrl; @@ -103,7 +102,7 @@ module.exports = { getExtensionDetails: async () => { extensionInitialUrl = await playwright.metamaskWindow().url(); extensionId = extensionInitialUrl.match('//(.*?)/')[1]; - extensionHomeUrl = `chrome-extension://${extensionId}/home.html`; + extensionHomeUrl = `chrome-extension://${extensionId}/notification.html`; extensionSettingsUrl = `${extensionHomeUrl}#settings`; extensionAdvancedSettingsUrl = `${extensionSettingsUrl}/advanced`; extensionExperimentalSettingsUrl = `${extensionSettingsUrl}/experimental`; @@ -124,22 +123,9 @@ module.exports = { extensionImportTokenUrl, }; }, - // workaround for metamask random blank page on first run + // Phantom doesn't need this, it's well coded :) fixBlankPage: async () => { - await playwright.metamaskWindow().waitForTimeout(1000); - for (let times = 0; times < 5; times++) { - if ( - !(await playwright - .metamaskWindow() - .locator(welcomePageElements.app) - .isVisible()) - ) { - await playwright.metamaskWindow().reload(); - await playwright.metamaskWindow().waitForTimeout(2000); - } else { - break; - } - } + return; }, confirmWelcomePage: async () => { await module.exports.fixBlankPage(); @@ -155,38 +141,43 @@ module.exports = { closePopupAndTooltips: async () => { // note: this is required for fast execution of e2e tests to avoid flakiness // otherwise popup may not be detected properly and not closed - await playwright.metamaskWindow().waitForTimeout(1000); - if ( - await playwright - .metamaskWindow() - .locator(mainPageElements.popup.container) - .isVisible() - ) { - const popupBackground = playwright - .metamaskWindow() - .locator(mainPageElements.popup.background); - const popupBackgroundBox = await popupBackground.boundingBox(); - await playwright - .metamaskWindow() - .mouse.click(popupBackgroundBox.x + 1, popupBackgroundBox.y + 1); - } - if ( - await playwright - .metamaskWindow() - .locator(mainPageElements.tippyTooltip.closeButton) - .isVisible() - ) { - await playwright.waitAndClick(mainPageElements.tippyTooltip.closeButton); - } - if ( - await playwright - .metamaskWindow() - .locator(mainPageElements.actionableMessage.closeButton) - .isVisible() - ) { - await playwright.waitAndClick( - mainPageElements.actionableMessage.closeButton, - ); + try { + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.popup.container) + .isVisible() + ) { + const popupBackground = playwright + .metamaskWindow() + .locator(mainPageElements.popup.background); + const popupBackgroundBox = await popupBackground.boundingBox(); + await playwright + .metamaskWindow() + .mouse.click(popupBackgroundBox.x + 1, popupBackgroundBox.y + 1); + } + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.tippyTooltip.closeButton) + .isVisible() + ) { + await playwright.waitAndClick( + mainPageElements.tippyTooltip.closeButton, + ); + } + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.actionableMessage.closeButton) + .isVisible() + ) { + await playwright.waitAndClick( + mainPageElements.actionableMessage.closeButton, + ); + } + } catch (ex) { + return true; } return true; }, @@ -230,14 +221,9 @@ module.exports = { return true; }, importWallet: async (secretWords, password) => { - await module.exports.optOutAnalytics(); - await playwright.waitAndClick( - firstTimeFlowPageElements.importWalletButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); + await playwright.waitAndClick(firstTimeFlowPageElements.importWalletButton); + + // STEP: Input mnemonic words and click Import // todo: add support for more secret words (15/18/21/24) for (const [index, word] of secretWords.split(' ').entries()) { await playwright.waitAndType( @@ -245,6 +231,19 @@ module.exports = { word, ); } + await playwright.waitAndClick( + firstTimeFlowImportPageElements.confirmWordsButton, + ); + + // STEP: Wait for confirm input + // shortcut confirmation + await new Promise(resolve => setTimeout(resolve, 200)); // the transitioning is too fast + await playwright.waitAndClick( + firstTimeFlowImportPageElements.confirmWordsButton, + // 'button:text("Import Selected Accounts")', + ); + + // STEP: Input password, confirm and continue await playwright.waitAndType( firstTimeFlowImportPageElements.passwordInput, password, @@ -256,20 +255,22 @@ module.exports = { await playwright.waitAndClick( firstTimeFlowImportPageElements.termsCheckbox, ); + // continue to next screen await playwright.waitAndClick( - firstTimeFlowImportPageElements.importButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, + firstTimeFlowImportPageElements.continueAfterPasswordButton, ); + // shortcut confirmation + await new Promise(resolve => setTimeout(resolve, 1000)); // the transitioning is too fast await playwright.waitAndClick( - endOfFlowPageElements.allDoneButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, + firstTimeFlowImportPageElements.continueOnShortcutConfirm, + ); + // finish + await new Promise(resolve => setTimeout(resolve, 1000)); // the transitioning is too fast + await playwright.waitAndClick( + firstTimeFlowImportPageElements.continueOnShortcutConfirm, ); + + await new Promise(resolve => setTimeout(resolve, 1000)); // the transitioning is too fast await module.exports.closePopupAndTooltips(); return true; }, @@ -318,7 +319,7 @@ module.exports = { return true; }, importAccount: async privateKey => { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await module.exports.goToImportAccount(); await playwright.waitAndType( mainPageElements.importAccount.input, @@ -339,7 +340,7 @@ module.exports = { if (accountName) { accountName = accountName.toLowerCase(); } - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await module.exports.goToNewAccount(); if (accountName) { await playwright.waitAndType( @@ -356,7 +357,7 @@ module.exports = { if (typeof accountNameOrAccountNumber === 'string') { accountNameOrAccountNumber = accountNameOrAccountNumber.toLowerCase(); } - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); // note: closePopupAndTooltips() is required after changing createAccount() to use direct urls (popup started appearing) // ^ this change also introduced 500ms delay for closePopupAndTooltips() function await module.exports.closePopupAndTooltips(); @@ -376,7 +377,7 @@ module.exports = { return true; }, changeNetwork: async network => { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await playwright.waitAndClick(mainPageElements.networkSwitcher.button); if (typeof network === 'string') { network = network.toLowerCase(); @@ -423,7 +424,7 @@ module.exports = { return true; }, addNetwork: async network => { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); if ( process.env.NETWORK_NAME && process.env.RPC_URL && @@ -485,7 +486,7 @@ module.exports = { return true; }, async disconnectWalletFromDapp() { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await playwright.waitAndClick(mainPageElements.optionsMenu.button); await playwright.waitAndClick( mainPageElements.optionsMenu.connectedSitesButton, @@ -515,7 +516,7 @@ module.exports = { return true; }, async disconnectWalletFromAllDapps() { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await playwright.waitAndClick(mainPageElements.optionsMenu.button); await playwright.waitAndClick( mainPageElements.optionsMenu.connectedSitesButton, @@ -611,7 +612,7 @@ module.exports = { ); }, resetAccount: async () => { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await module.exports.goToAdvancedSettings(); await playwright.waitAndClick(advancedPageElements.resetAccountButton); await playwright.waitAndClick(resetAccountModalElements.resetButton); @@ -686,7 +687,7 @@ module.exports = { }, importToken: async tokenConfig => { let tokenData = {}; - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await module.exports.goToImportToken(); if (typeof tokenConfig === 'string') { await playwright.waitAndType( @@ -1120,15 +1121,13 @@ module.exports = { return true; }, getWalletAddress: async () => { - await switchToMetamaskIfNotActive(); - await playwright.waitAndClick(mainPageElements.optionsMenu.button); - await playwright.waitAndClick( - mainPageElements.optionsMenu.accountDetailsButton, - ); - walletAddress = await playwright.waitAndGetValue( - mainPageElements.accountModal.walletAddressInput, - ); - await playwright.waitAndClick(mainPageElements.accountModal.closeButton); + await switchToPhantomIfNotActive(); + await playwright.metamaskWindow().hover(mainPageElements.accountBar.title); + await new Promise(resolve => setTimeout(resolve, 1000)); + await playwright.waitAndClick(mainPageElements.accountBar.ethRow); + walletAddress = await playwright + .metamaskWindow() + .evaluate('navigator.clipboard.readText()'); await switchToCypressIfNotActive(); return walletAddress; }, @@ -1149,13 +1148,14 @@ module.exports = { await playwright.assignActiveTabName('metamask'); await module.exports.getExtensionDetails(); await module.exports.fixBlankPage(); + + // password (unlock?) or new set up? if ( await playwright .metamaskWindow() - .locator(welcomePageElements.confirmButton) + .locator(firstTimeFlowPageElements.importWalletButton) .isVisible() ) { - await module.exports.confirmWelcomePage(); if (secretWordsOrPrivateKey.includes(' ')) { // secret words await module.exports.importWallet(secretWordsOrPrivateKey, password); @@ -1165,12 +1165,23 @@ module.exports = { await module.exports.importAccount(secretWordsOrPrivateKey); } - await setupSettings(enableAdvancedSettings); + // await setupSettings(enableAdvancedSettings); + // if (isCustomNetwork) { + // await module.exports.addNetwork(network); + // } else { + // await module.exports.changeNetwork(network); + // } + await switchToPhantomIfNotActive(); + // await module.exports.goToHome(); - if (isCustomNetwork) { - await module.exports.addNetwork(network); - } else { - await module.exports.changeNetwork(network); + if ( + await playwright + .metamaskWindow() + .locator(mainPageElements.whatsNew.header) + ) { + await playwright + .metamaskWindow() + .click(mainPageElements.whatsNew.continueButton); } walletAddress = await module.exports.getWalletAddress(); await playwright.switchToCypressWindow(); @@ -1191,20 +1202,21 @@ module.exports = { .metamaskWindow() .locator(mainPageElements.walletOverview) .isVisible()) && - !process.env.RESET_METAMASK + !process.env.RESET_PHANTOM ) { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); walletAddress = await module.exports.getWalletAddress(); await playwright.switchToCypressWindow(); return true; } else { - // todo: reset metamask state + // todo: reset phantom state } } }, }; -async function switchToMetamaskIfNotActive() { +async function switchToPhantomIfNotActive() { + await playwright.switchToMetamaskWindow(); if (await playwright.isCypressWindowActive()) { await playwright.switchToMetamaskWindow(); switchBackToCypressWindow = true; @@ -1227,7 +1239,7 @@ async function activateAdvancedSetting( experimental, ) { if (!skipSetup) { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); if (experimental) { await module.exports.goToExperimentalSettings(); } else { @@ -1252,7 +1264,7 @@ async function activateAdvancedSetting( } async function setupSettings(enableAdvancedSettings) { - await switchToMetamaskIfNotActive(); + await switchToPhantomIfNotActive(); await module.exports.goToAdvancedSettings(); await module.exports.activateAdvancedGasControl(true); await module.exports.activateShowHexData(true); diff --git a/commands/playwright.js b/commands/playwright.js index d922338..8a8be8b 100644 --- a/commands/playwright.js +++ b/commands/playwright.js @@ -103,6 +103,18 @@ module.exports = { return true; }, switchToMetamaskWindow: async () => { + if (metamaskWindow.isClosed()) { + const newPage = await browser.contexts()[0].newPage(); + await Promise.all([ + newPage.waitForNavigation(), + newPage.goto( + metamaskWindow.url().replace('onboarding.html', 'popup.html'), + ), + ]); + // await newPage.waitUntilStable(); + metamaskWindow = newPage; + } + await metamaskWindow.bringToFront(); await module.exports.assignActiveTabName('metamask'); return true; diff --git a/pages/phantom/confirmation-page.js b/pages/phantom/confirmation-page.js new file mode 100644 index 0000000..b19a1b5 --- /dev/null +++ b/pages/phantom/confirmation-page.js @@ -0,0 +1,12 @@ +const confirmationPage = '.confirmation-page'; +const confirmationPageFooter = `${confirmationPage} .confirmation-footer`; +const footer = { + footer: confirmationPageFooter, + cancelButton: `${confirmationPageFooter} .btn-secondary`, + approveButton: `${confirmationPageFooter} .btn-primary`, +}; + +module.exports.confirmationPageElements = { + confirmationPage, + footer, +}; diff --git a/pages/phantom/first-time-flow-page.js b/pages/phantom/first-time-flow-page.js new file mode 100644 index 0000000..bb80bd9 --- /dev/null +++ b/pages/phantom/first-time-flow-page.js @@ -0,0 +1,85 @@ +const app = '#root'; +const welcomePage = '#root'; +const confirmButton = `${welcomePage} .first-time-flow__button`; +module.exports.welcomePageElements = { + app, + welcomePage, + confirmButton, +}; + +const metametricsPage = '.metametrics-opt-in'; +const optOutAnalyticsButton = `${metametricsPage} [data-testid="page-container-footer-cancel"]`; +module.exports.metametricsPageElements = { + metametricsPage, + optOutAnalyticsButton, +}; + +const firstTimeFlowPage = '.first-time-flow'; +const importWalletButton = `[data-testid="import-recovery-phrase-button"]`; +const createWalletButton = `${firstTimeFlowPage} [data-testid="create-wallet-button"]`; +module.exports.firstTimeFlowPageElements = { + firstTimeFlowPage, + importWalletButton, + createWalletButton, +}; + +const firstTimeFlowImportPage = '.first-time-flow__import'; +const newVaultForm = `${firstTimeFlowImportPage} .create-new-vault__form`; +const secretWordsInput = number => + `[data-testid="secret-recovery-phrase-word-input-${number}"]`; +const confirmWordsButton = `[data-testid="onboarding-form-submit-button"]`; +const passwordInput = `[data-testid="onboarding-form-password-input"]`; +const confirmPasswordInput = `[data-testid="onboarding-form-confirm-password-input"]`; +const termsCheckbox = `[data-testid="onboarding-form-terms-of-service-checkbox"]`; +const continueAfterPasswordButton = + '[data-testid="onboarding-form-submit-button"]'; +const continueOnShortcutConfirm = + '[data-testid="onboarding-form-submit-button"]'; +const importButton = `${newVaultForm} .create-new-vault__submit-button`; + +module.exports.firstTimeFlowImportPageElements = { + firstTimeFlowImportPage, + newVaultForm, + secretWordsInput, + passwordInput, + confirmPasswordInput, + termsCheckbox, + importButton, + confirmWordsButton, + continueAfterPasswordButton, + continueOnShortcutConfirm, +}; + +const firstTimeFlowCreatePage = '.first-time-flow'; +const newPasswordInput = `${firstTimeFlowCreatePage} [data-testid="create-password"]`; +const confirmNewPasswordInput = `${firstTimeFlowCreatePage} [data-testid="confirm-password"]`; +const newSignupCheckbox = `${firstTimeFlowCreatePage} .first-time-flow__checkbox`; +const createButton = `${firstTimeFlowCreatePage} .first-time-flow__button`; +module.exports.firstTimeFlowCreatePagePageElements = { + firstTimeFlowCreatePage, + newPasswordInput, + confirmNewPasswordInput, + newSignupCheckbox, + createButton, +}; + +const secureYourWalletPage = '[data-testid="seed-phrase-intro"]'; +const nextButton = `${secureYourWalletPage} button`; +module.exports.secureYourWalletPageElements = { + secureYourWalletPage, + nextButton, +}; + +const revealSeedPage = '[data-testid="reveal-seed-phrase"]'; +const remindLaterButton = `${revealSeedPage} .first-time-flow__button`; +module.exports.revealSeedPageElements = { + revealSeedPage, + remindLaterButton, +}; + +const endOfFlowPage = '[data-testid="end-of-flow"]'; +const allDoneButton = `${endOfFlowPage} [data-testid="EOF-complete-button"]`; +module.exports.endOfFlowPageElements = { + endOfFlowPage, + allDoneButton, +}; diff --git a/pages/phantom/main-page.js b/pages/phantom/main-page.js new file mode 100644 index 0000000..e978cb2 --- /dev/null +++ b/pages/phantom/main-page.js @@ -0,0 +1,153 @@ +const networkSwitcherButtonSelector = '.network-display'; +const networkSwitcher = { + button: networkSwitcherButtonSelector, + networkName: `${networkSwitcherButtonSelector} .typography`, + dropdownMenu: '[data-testid="network-droppo"]', + dropdownMenuItem: `[data-testid="network-droppo"] .dropdown-menu-item`, + mainnetNetworkItem: `[data-testid="network-droppo"] [data-testid="mainnet-network-item"]`, + goerliNetworkItem: `[data-testid="network-droppo"] [data-testid="goerli-network-item"]`, + sepoliaNetworkItem: `[data-testid="network-droppo"] [data-testid="sepolia-network-item"]`, + localhostNetworkItem: `[data-testid="network-droppo"] [data-testid="Localhost 8545-network-item"]`, + networkButton: number => + `[data-testid="network-droppo"] .dropdown-menu-item:nth-child(${ + 3 + number + })`, +}; + +const walletOverview = '.wallet-overview'; + +const tabs = { + assetsButton: '[data-testid="home__asset-tab"] button', + activityButton: '[data-testid="home__activity-tab"] button', +}; + +const transactionList = '.transaction-list__transactions'; +const pendingTransactionsList = `${transactionList} .transaction-list__pending-transactions`; +const completedTransactionsList = `${transactionList} .transaction-list__completed-transactions`; +const activityTab = { + transactionList, + pendingTransactionsList, + completedTransactionsList, + unconfirmedTransaction: `${pendingTransactionsList} .transaction-list-item--unconfirmed`, + confirmedTransaction: `${completedTransactionsList} .transaction-list-item`, +}; + +const popupSelector = '.popover-container'; +const sendPopupSelector = `${popupSelector} .transaction-list-item-details`; +const popup = { + container: popupSelector, + closeButton: '.popover-header__button', + background: '.popover-bg', + sendPopup: { + container: sendPopupSelector, + speedUpButton: `${sendPopupSelector} .btn-primary`, + cancelButton: `${sendPopupSelector} .btn-secondary`, + transactionStatus: `${sendPopupSelector} .transaction-status`, + copyTxIdButton: `${sendPopupSelector} .transaction-list-item-details__tx-hash .transaction-list-item-details__header-button a`, + // todo: + }, +}; + +const tippyTooltipSelector = '.tippy-popper'; +const tippyTooltip = { + container: tippyTooltipSelector, + closeButton: `${tippyTooltipSelector} button`, +}; + +const actionableMessageSelector = '.actionable-message'; +const actionableMessage = { + container: actionableMessageSelector, + closeButton: `${actionableMessageSelector} button`, +}; + +const accountMenu = { + button: '.account-menu__icon', + accountButton: number => `.account-menu__account:nth-child(${number})`, + accountName: '.account-menu__name', + createAccountButton: '.account-menu__item--clickable:nth-child(6)', + importAccountButton: '.account-menu__item--clickable:nth-child(7)', + settingsButton: '.account-menu__item--clickable:nth-child(11)', +}; + +const optionsMenu = { + button: '[data-testid=account-options-menu-button]', + accountDetailsButton: '[data-testid="account-options-menu__account-details"]', + connectedSitesButton: '[data-testid="account-options-menu__connected-sites"]', +}; + +const whatsNew = { + header: '[data-testid="whats_new-header"]', + continueButton: '[data-testid="whats_new-continue_button"]', +}; + +const accountBar = { + title: '[data-testid="tooltip_interactive-wrapper"]', + ethRow: '[data-testid="account-header-chain-eip155:1"]', +}; + +const connectedSitesSelector = '.connected-sites'; +const connectedSites = { + modal: connectedSitesSelector, + disconnectLabel: `${connectedSitesSelector} .connected-sites-list__content-row-link-button`, + cancelButton: `${connectedSitesSelector} .btn-secondary`, + disconnectButton: `${connectedSitesSelector} .btn-primary`, + closeButton: `${connectedSitesSelector} [data-testid="popover-close"]`, +}; + +const accountModal = { + walletAddressInput: '.account-modal .qr-code__address', + closeButton: '.account-modal__close', +}; + +const importAccountSelector = '.new-account'; +const importAccount = { + page: importAccountSelector, + input: `${importAccountSelector} #private-key-box`, + cancelButton: `${importAccountSelector} .new-account-create-form__button:nth-child(1)`, + importButton: `${importAccountSelector} .new-account-create-form__button:nth-child(2)`, +}; + +const createAccount = { + page: importAccountSelector, + input: `${importAccountSelector} .new-account-create-form__input`, + cancelButton: `${importAccountSelector} .new-account-create-form__button:nth-child(1)`, + createButton: `${importAccountSelector} .new-account-create-form__button:nth-child(2)`, +}; + +const importTokenFormSelector = '.import-token__custom-token-form'; +const importToken = { + form: importTokenFormSelector, + tokenContractAddressInput: `${importTokenFormSelector} #custom-address`, + tokenSymbolInput: `${importTokenFormSelector} #custom-symbol`, + tokenEditButton: `${importTokenFormSelector} .import-token__custom-symbol__edit`, + tokenDecimalInput: `${importTokenFormSelector} #custom-decimals`, + addCustomTokenButton: `[data-testid="page-container-footer-next"]`, + confirmImportTokenContent: '.confirm-import-token', + importTokensButton: `.btn-primary`, +}; + +const assetNavigationSelector = '.asset-navigation'; +const asset = { + navigation: assetNavigationSelector, + backButton: `${assetNavigationSelector} [data-testid="asset__back"]`, +}; + +module.exports.mainPageElements = { + networkSwitcher, + walletOverview, + tabs, + activityTab, + popup, + tippyTooltip, + actionableMessage, + accountMenu, + accountBar, + optionsMenu, + connectedSites, + accountModal, + importAccount, + createAccount, + importToken, + asset, + whatsNew, +}; diff --git a/pages/phantom/notification-page.js b/pages/phantom/notification-page.js new file mode 100644 index 0000000..7844b44 --- /dev/null +++ b/pages/phantom/notification-page.js @@ -0,0 +1,146 @@ +const notificationPage = '.notification'; +const notificationAppContent = `${notificationPage} #app-content .app`; +const loadingLogo = `${notificationPage} #loading__logo`; +const loadingSpinner = `${notificationPage} #loading__spinner`; +const nextButton = `${notificationPage} .permissions-connect-choose-account__bottom-buttons .btn-primary`; +const allowToSpendButton = `${notificationPage} [data-testid="page-container-footer-next"]`; +const rejectToSpendButton = `${notificationPage} [data-testid="page-container-footer-cancel"]`; +const selectAllCheckbox = `${notificationPage} .choose-account-list__header-check-box`; +module.exports.notificationPageElements = { + notificationPage, + notificationAppContent, + loadingLogo, + loadingSpinner, + nextButton, + allowToSpendButton, + rejectToSpendButton, + selectAllCheckbox, +}; + +const confirmSignatureRequestButton = `${notificationPage} .request-signature__footer__sign-button`; +const rejectSignatureRequestButton = `${notificationPage} .request-signature__footer__cancel-button`; +const signatureRequestScrollDownButton = `${notificationPage} [data-testid="signature-request-scroll-button"]`; +module.exports.signaturePageElements = { + confirmSignatureRequestButton, + rejectSignatureRequestButton, + signatureRequestScrollDownButton, +}; + +const confirmDataSignatureRequestButton = `${notificationPage} [data-testid="signature-sign-button"]`; +const rejectDataSignatureRequestButton = `${notificationPage} [data-testid="signature-cancel-button"]`; +module.exports.dataSignaturePageElements = { + confirmDataSignatureRequestButton, + rejectDataSignatureRequestButton, + signatureRequestScrollDownButton, +}; + +const permissionsPage = '.permissions-connect'; +const connectButton = `${permissionsPage} .permission-approval-container__footers .btn-primary`; +module.exports.permissionsPageElements = { + permissionsPage, + connectButton, +}; + +const popupContainer = '.popover-container'; +const popupCloseButton = `${popupContainer} .popover-header__button`; +const popupCopyRecipientPublicAddressButton = `${popupContainer} .nickname-popover__public-address button`; +const recipientPublicAddress = `${popupContainer} .nickname-popover__public-address__constant`; +module.exports.recipientPopupElements = { + popupContainer, + popupCloseButton, + popupCopyRecipientPublicAddressButton, + recipientPublicAddress, +}; + +const confirmPageHeader = `${notificationPage} .confirm-page-container-header`; +const confirmPageContent = `${notificationPage} .confirm-page-container-content`; +const networkLabel = `${confirmPageHeader} .network-display`; +const senderButton = `${confirmPageHeader} .sender-to-recipient__party--sender`; +const recipientButton = `${confirmPageHeader} .sender-to-recipient__party--recipient-with-address`; +const editGasFeeLegacyButton = `${notificationPage} .transaction-detail-edit button`; +const editGasFeeLegacyOverrideAckButton = `${notificationPage} .edit-gas-display .edit-gas-display__dapp-acknowledgement-button`; +const editGasLegacyPopup = `${notificationPage} .edit-gas-popover__wrapper`; +const advancedLegacyGasControls = `${editGasLegacyPopup} .edit-gas-display .advanced-gas-controls`; +const gasLimitLegacyInput = `${advancedLegacyGasControls} .form-field:nth-child(1) input`; +const gasPriceLegacyInput = `${advancedLegacyGasControls} .form-field:nth-child(2) input`; +const saveLegacyButton = `${editGasLegacyPopup} .popover-footer .btn-primary`; +const editGasFeeButton = `${notificationPage} [data-testid="edit-gas-fee-button"]`; +const gasOptionLowButton = `${notificationPage} [data-testid="edit-gas-fee-item-low"]`; +const gasOptionMediumButton = `${notificationPage} [data-testid="edit-gas-fee-item-medium"]`; +const gasOptionHighButton = `${notificationPage} [data-testid="edit-gas-fee-item-high"]`; +const gasOptionDappSuggestedButton = `${notificationPage} [data-testid="edit-gas-fee-item-dappSuggested"]`; +const gasOptionCustomButton = `${notificationPage} [data-testid="edit-gas-fee-item-custom"]`; +const baseFeeInput = `${notificationPage} [data-testid="base-fee-input"]`; +const priorityFeeInput = `${notificationPage} [data-testid="priority-fee-input"]`; +const editGasLimitButton = `${notificationPage} [data-testid="advanced-gas-fee-edit"]`; +const gasLimitInput = `${notificationPage} [data-testid="gas-limit-input"]`; +const saveCustomGasFeeButton = `${notificationPage} .popover-container .btn-primary`; +const totalLabel = `${confirmPageContent} .transaction-detail-item:nth-child(2) .transaction-detail-item__detail-values h6:nth-child(2)`; // todo: fix +const customNonceInput = `${confirmPageContent} .custom-nonce-input input`; +const tabs = `${confirmPageContent} .tabs`; +const detailsButton = `${tabs} .tab:nth-child(1) button`; +const dataButton = `${tabs} .tab:nth-child(2) button`; +const dataTab = `${tabs} .confirm-page-container-content__data`; +const originValue = `${dataTab} .confirm-page-container-content__data-box:nth-child(1) .confirm-page-container-content__data-field:nth-child(1) div:nth-child(2)`; +const bytesValue = `${dataTab} .confirm-page-container-content__data-box:nth-child(1) .confirm-page-container-content__data-field:nth-child(2) div:nth-child(2)`; +const hexDataValue = `${dataTab} .confirm-page-container-content__data-box:nth-child(3)`; +const rejectButton = `${confirmPageContent} [data-testid="page-container-footer-cancel"]`; +const confirmButton = `${confirmPageContent} [data-testid="page-container-footer-next"]`; +module.exports.confirmPageElements = { + notificationPage, + confirmPageHeader, + confirmPageContent, + networkLabel, + senderButton, + recipientButton, + editGasFeeLegacyButton, + editGasFeeLegacyOverrideAckButton, + editGasLegacyPopup, + advancedLegacyGasControls, + gasLimitLegacyInput, + gasPriceLegacyInput, + saveLegacyButton, + editGasFeeButton, + gasOptionLowButton, + gasOptionMediumButton, + gasOptionHighButton, + gasOptionDappSuggestedButton, + gasOptionCustomButton, + baseFeeInput, + priorityFeeInput, + editGasLimitButton, + gasLimitInput, + saveCustomGasFeeButton, + totalLabel, + customNonceInput, + tabs, + detailsButton, + dataButton, + dataTab, + originValue, + bytesValue, + hexDataValue, + rejectButton, + confirmButton, +}; + +const confirmEncryptionPublicKeyButton = `${notificationPage} .request-encryption-public-key__footer__sign-button`; +const rejectEncryptionPublicKeyButton = `${notificationPage} .request-encryption-public-key__footer__cancel-button`; +module.exports.encryptionPublicKeyPageElements = { + confirmEncryptionPublicKeyButton, + rejectEncryptionPublicKeyButton, +}; + +const confirmDecryptionRequestButton = `${notificationPage} .request-decrypt-message__footer__sign-button`; +const rejectDecryptionRequestButton = `${notificationPage} .request-decrypt-message__footer__cancel-button`; +module.exports.decryptPageElements = { + confirmDecryptionRequestButton, + rejectDecryptionRequestButton, +}; + +const confirmAddTokenButton = `${notificationPage} .btn-primary`; +const rejectAddTokenButton = `${notificationPage} .btn-secondary`; +module.exports.addTokenPageElements = { + confirmAddTokenButton, + rejectAddTokenButton, +}; diff --git a/pages/phantom/page.js b/pages/phantom/page.js new file mode 100644 index 0000000..2a5e465 --- /dev/null +++ b/pages/phantom/page.js @@ -0,0 +1,16 @@ +const loadingLogo = '.loading-logo'; +const loadingSpinner = '.loading-spinner'; +const loadingOverlay = '.loading-overlay'; +const loadingOverlaySpinner = '.loading-overlay__spinner'; +const loadingOverlayErrorButtons = '.loading-overlay__error-buttons'; +const loadingOverlayErrorButtonsRetryButton = + '.loading-overlay__error-buttons .btn-primary'; + +module.exports.pageElements = { + loadingLogo, + loadingSpinner, + loadingOverlay, + loadingOverlaySpinner, + loadingOverlayErrorButtons, + loadingOverlayErrorButtonsRetryButton, +}; diff --git a/pages/phantom/settings-page.js b/pages/phantom/settings-page.js new file mode 100644 index 0000000..bcaee5b --- /dev/null +++ b/pages/phantom/settings-page.js @@ -0,0 +1,94 @@ +const settingsPage = '.settings-page'; +const advancedButton = `${settingsPage} button:nth-child(2)`; +const networksButton = `${settingsPage} button:nth-child(6)`; +const closeButton = `${settingsPage} .settings-page__header__title-container__close-button`; +module.exports.settingsPageElements = { + settingsPage, + advancedButton, + networksButton, + closeButton, +}; + +const resetAccountButton = + '[data-testid="advanced-setting-reset-account"] button'; +const advancedGasControlToggleOn = + '[data-testid="advanced-setting-advanced-gas-inline"] .toggle-button--on'; +const advancedGasControlToggleOff = + '[data-testid="advanced-setting-advanced-gas-inline"] .toggle-button--off'; +const enhancedTokenDetectionToggleOn = + '[data-testid="advanced-setting-token-detection"] .toggle-button--on'; +const enhancedTokenDetectionToggleOff = + '[data-testid="advanced-setting-token-detection"] .toggle-button--off'; +const showHexDataToggleOn = + '[data-testid="advanced-setting-hex-data"] .toggle-button--on'; +const showHexDataToggleOff = + '[data-testid="advanced-setting-hex-data"] .toggle-button--off'; +const showTestnetConversionOn = + '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(7) .toggle-button--on'; +const showTestnetConversionOff = + '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(7) .toggle-button--off'; +const showTestnetNetworksOn = + '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(8) .toggle-button--on'; +const showTestnetNetworksOff = + '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(8) .toggle-button--off'; +const customNonceToggleOn = + '[data-testid="advanced-setting-custom-nonce"] .toggle-button--on'; +const customNonceToggleOff = + '[data-testid="advanced-setting-custom-nonce"] .toggle-button--off'; +const dismissBackupReminderOn = + '[data-testid="advanced-setting-dismiss-reminder"] .toggle-button--on'; +const dismissBackupReminderOff = + '[data-testid="advanced-setting-dismiss-reminder"] .toggle-button--off'; +module.exports.advancedPageElements = { + resetAccountButton, + advancedGasControlToggleOn, + advancedGasControlToggleOff, + enhancedTokenDetectionToggleOn, + enhancedTokenDetectionToggleOff, + showHexDataToggleOn, + showHexDataToggleOff, + showTestnetConversionOn, + showTestnetConversionOff, + showTestnetNetworksOn, + showTestnetNetworksOff, + dismissBackupReminderOn, + dismissBackupReminderOff, + customNonceToggleOn, + customNonceToggleOff, +}; + +const enhancedGasFeeUIToggleOn = + '.settings-page__content-row:nth-child(1) .toggle-button--on'; +const enhancedGasFeeUIToggleOff = + '.settings-page__content-row:nth-child(1) .toggle-button--off'; +module.exports.experimentalSettingsPageElements = { + enhancedGasFeeUIToggleOn, + enhancedGasFeeUIToggleOff, +}; + +const nevermindButton = '.modal-container .btn-secondary'; +const resetButton = '.modal-container .btn-danger-primary'; +module.exports.resetAccountModalElements = { + nevermindButton, + resetButton, +}; + +const addNetworkButton = '.networks-tab__body button'; +module.exports.networksPageElements = { addNetworkButton }; + +const addNetworkForm = '.networks-tab__add-network-form-body'; +const networkNameInput = `${addNetworkForm} .form-field:nth-child(1) input`; +const rpcUrlInput = `${addNetworkForm} .form-field:nth-child(2) input`; +const chainIdInput = `${addNetworkForm} .form-field:nth-child(3) input`; +const symbolInput = `${addNetworkForm} .form-field:nth-child(4) input`; +const blockExplorerInput = `${addNetworkForm} .form-field:nth-child(5) input`; +const saveButton = '.networks-tab__add-network-form-footer .btn-primary'; +module.exports.addNetworkPageElements = { + addNetworkForm, + networkNameInput, + rpcUrlInput, + chainIdInput, + symbolInput, + blockExplorerInput, + saveButton, +}; diff --git a/pages/phantom/unlock-page.js b/pages/phantom/unlock-page.js new file mode 100644 index 0000000..69e65f4 --- /dev/null +++ b/pages/phantom/unlock-page.js @@ -0,0 +1,9 @@ +const unlockPage = '[data-testid="unlock-page"]'; +const passwordInput = `${unlockPage} #password`; +const unlockButton = `${unlockPage} .btn-default`; + +module.exports.unlockPageElements = { + unlockPage, + passwordInput, + unlockButton, +}; diff --git a/plugins/index.js b/plugins/index.js index 5078985..a6c02b1 100644 --- a/plugins/index.js +++ b/plugins/index.js @@ -32,6 +32,20 @@ module.exports = (on, config) => { '--disable-backgrounding-occluded-windows', '--disable-renderer-backgrounding', ); + arguments_.preferences.default.profile = { + content_settings: { + exceptions: { + clipboard: { + '*': { + expiration: '0', + last_modified: '13248200230459161', + model: 0, + setting: 1, + }, + }, + }, + }, + }; } if (!process.env.SKIP_METAMASK_INSTALL) { // NOTE: extensions cannot be loaded in headless Chrome From cc6628871897052398becbfa4e7e30b0d69d3282 Mon Sep 17 00:00:00 2001 From: Maxim Geerinck Date: Thu, 9 Feb 2023 16:39:08 +0100 Subject: [PATCH 3/5] chore: Add phantom selectors --- commands/phantom.js | 1079 +++------------------------- commands/playwright.js | 49 +- commands/synthetix.js | 5 +- pages/phantom/notification-page.js | 45 ++ pages/phantom/settings-page.js | 95 +-- pages/phantom/unlock-page.js | 9 +- plugins/index.js | 141 ++-- support/commands.js | 65 +- 8 files changed, 326 insertions(+), 1162 deletions(-) diff --git a/commands/phantom.js b/commands/phantom.js index 13e5792..c5d710b 100644 --- a/commands/phantom.js +++ b/commands/phantom.js @@ -22,6 +22,10 @@ const { dataSignaturePageElements, recipientPopupElements, addTokenPageElements, + buttons, + transactionPageElements, + menu, + incorrectModePageElements, } = require('../pages/phantom/notification-page'); const { settingsPageElements, @@ -127,97 +131,9 @@ module.exports = { fixBlankPage: async () => { return; }, - confirmWelcomePage: async () => { - await module.exports.fixBlankPage(); - await playwright.waitAndClick( - welcomePageElements.confirmButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - return true; - }, - closePopupAndTooltips: async () => { - // note: this is required for fast execution of e2e tests to avoid flakiness - // otherwise popup may not be detected properly and not closed - try { - if ( - await playwright - .metamaskWindow() - .locator(mainPageElements.popup.container) - .isVisible() - ) { - const popupBackground = playwright - .metamaskWindow() - .locator(mainPageElements.popup.background); - const popupBackgroundBox = await popupBackground.boundingBox(); - await playwright - .metamaskWindow() - .mouse.click(popupBackgroundBox.x + 1, popupBackgroundBox.y + 1); - } - if ( - await playwright - .metamaskWindow() - .locator(mainPageElements.tippyTooltip.closeButton) - .isVisible() - ) { - await playwright.waitAndClick( - mainPageElements.tippyTooltip.closeButton, - ); - } - if ( - await playwright - .metamaskWindow() - .locator(mainPageElements.actionableMessage.closeButton) - .isVisible() - ) { - await playwright.waitAndClick( - mainPageElements.actionableMessage.closeButton, - ); - } - } catch (ex) { - return true; - } - return true; - }, - closeModal: async () => { - // note: this is required for fast execution of e2e tests to avoid flakiness - // otherwise modal may not be detected properly and not closed - await playwright.metamaskWindow().waitForTimeout(1000); - if ( - await playwright - .metamaskWindow() - .locator(mainPageElements.connectedSites.modal) - .isVisible() - ) { - await playwright.waitAndClick( - mainPageElements.connectedSites.closeButton, - ); - } - return true; - }, - unlock: async password => { - await module.exports.fixBlankPage(); - await playwright.waitAndType(unlockPageElements.passwordInput, password); - await playwright.waitAndClick( - unlockPageElements.unlockButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); - return true; - }, - optOutAnalytics: async () => { - await playwright.waitAndClick( - metametricsPageElements.optOutAnalyticsButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); + acceptAccess: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick(buttons.primaryButton, notificationPage); return true; }, importWallet: async (secretWords, password) => { @@ -271,855 +187,47 @@ module.exports = { ); await new Promise(resolve => setTimeout(resolve, 1000)); // the transitioning is too fast - await module.exports.closePopupAndTooltips(); - return true; - }, - createWallet: async password => { - await module.exports.optOutAnalytics(); - await playwright.waitAndClick( - firstTimeFlowPageElements.createWalletButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await playwright.waitAndType( - firstTimeFlowCreatePagePageElements.newPasswordInput, - password, - ); - await playwright.waitAndType( - firstTimeFlowCreatePagePageElements.confirmNewPasswordInput, - password, - ); - await playwright.waitAndClick( - firstTimeFlowCreatePagePageElements.newSignupCheckbox, - ); - await playwright.waitAndClick( - firstTimeFlowCreatePagePageElements.createButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await playwright.waitAndClick( - secureYourWalletPageElements.nextButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await playwright.waitAndClick( - revealSeedPageElements.remindLaterButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); + // await module.exports.closePopupAndTooltips(); return true; }, - importAccount: async privateKey => { - await switchToPhantomIfNotActive(); - await module.exports.goToImportAccount(); - await playwright.waitAndType( - mainPageElements.importAccount.input, - privateKey, - ); - await playwright.waitAndClick( - mainPageElements.importAccount.importButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); - await switchToCypressIfNotActive(); - return true; - }, - createAccount: async accountName => { - if (accountName) { - accountName = accountName.toLowerCase(); - } - await switchToPhantomIfNotActive(); - await module.exports.goToNewAccount(); - if (accountName) { - await playwright.waitAndType( - mainPageElements.createAccount.input, - accountName, - ); - } - await playwright.waitAndClick(mainPageElements.createAccount.createButton); - await module.exports.closePopupAndTooltips(); - await switchToCypressIfNotActive(); - return true; - }, - switchAccount: async accountNameOrAccountNumber => { - if (typeof accountNameOrAccountNumber === 'string') { - accountNameOrAccountNumber = accountNameOrAccountNumber.toLowerCase(); - } - await switchToPhantomIfNotActive(); - // note: closePopupAndTooltips() is required after changing createAccount() to use direct urls (popup started appearing) - // ^ this change also introduced 500ms delay for closePopupAndTooltips() function - await module.exports.closePopupAndTooltips(); - await playwright.waitAndClick(mainPageElements.accountMenu.button); - if (typeof accountNameOrAccountNumber === 'number') { - await playwright.waitAndClick( - mainPageElements.accountMenu.accountButton(accountNameOrAccountNumber), - ); - } else { - await playwright.waitAndClickByText( - mainPageElements.accountMenu.accountName, - accountNameOrAccountNumber, - ); - } - await module.exports.closePopupAndTooltips(); - await switchToCypressIfNotActive(); - return true; - }, - changeNetwork: async network => { - await switchToPhantomIfNotActive(); - await playwright.waitAndClick(mainPageElements.networkSwitcher.button); - if (typeof network === 'string') { - network = network.toLowerCase(); - if (network === 'mainnet') { - await playwright.waitAndClick( - mainPageElements.networkSwitcher.mainnetNetworkItem, - ); - } else if (network === 'goerli') { - await playwright.waitAndClick( - mainPageElements.networkSwitcher.goerliNetworkItem, - ); - } else if (network === 'sepolia') { - await playwright.waitAndClick( - mainPageElements.networkSwitcher.sepoliaNetworkItem, - ); - } else if (network === 'localhost') { - await playwright.waitAndClick( - mainPageElements.networkSwitcher.localhostNetworkItem, - ); - } else { - await playwright.waitAndClickByText( - mainPageElements.networkSwitcher.dropdownMenuItem, - network, - ); - } - await playwright.waitForText( - mainPageElements.networkSwitcher.networkName, - network, - ); - } else if (typeof network === 'object') { - network.networkName = network.networkName.toLowerCase(); - await playwright.waitAndClickByText( - mainPageElements.networkSwitcher.dropdownMenuItem, - network.networkName, - ); - await playwright.waitForText( - mainPageElements.networkSwitcher.networkName, - network.networkName, - ); - } - await module.exports.closePopupAndTooltips(); - await setNetwork(network); - await switchToCypressIfNotActive(); - return true; - }, - addNetwork: async network => { - await switchToPhantomIfNotActive(); - if ( - process.env.NETWORK_NAME && - process.env.RPC_URL && - process.env.CHAIN_ID - ) { - network = { - networkName: process.env.NETWORK_NAME, - rpcUrl: process.env.RPC_URL, - chainId: process.env.CHAIN_ID, - symbol: process.env.SYMBOL, - blockExplorer: process.env.BLOCK_EXPLORER, - isTestnet: process.env.IS_TESTNET, - }; - } - if (typeof network === 'string') { - network = network.toLowerCase(); - } else if (typeof network === 'object') { - network.networkName = network.networkName.toLowerCase(); - } - await module.exports.goToAddNetwork(); - await playwright.waitAndType( - addNetworkPageElements.networkNameInput, - network.networkName, - ); - await playwright.waitAndType( - addNetworkPageElements.rpcUrlInput, - network.rpcUrl, - ); - await playwright.waitAndType( - addNetworkPageElements.chainIdInput, - network.chainId, - ); - if (network.symbol) { - await playwright.waitAndType( - addNetworkPageElements.symbolInput, - network.symbol, - ); - } - if (network.blockExplorer) { - await playwright.waitAndType( - addNetworkPageElements.blockExplorerInput, - network.blockExplorer, - ); - } - await playwright.waitAndClick( - addNetworkPageElements.saveButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); - await setNetwork(network); - await playwright.waitForText( - mainPageElements.networkSwitcher.networkName, - network.networkName, - ); - await switchToCypressIfNotActive(); - return true; - }, - async disconnectWalletFromDapp() { - await switchToPhantomIfNotActive(); - await playwright.waitAndClick(mainPageElements.optionsMenu.button); - await playwright.waitAndClick( - mainPageElements.optionsMenu.connectedSitesButton, - ); + closePopupAndTooltips: async () => { + // note: this is required for fast execution of e2e tests to avoid flakiness + // otherwise popup may not be detected properly and not closed + await playwright.metamaskWindow().waitForTimeout(1000); if ( await playwright .metamaskWindow() - .locator(mainPageElements.connectedSites.disconnectLabel) + .locator(mainPageElements.popup.container) .isVisible() ) { - console.log( - '[disconnectWalletFromDapp] Wallet is connected to a dapp, disconnecting..', - ); - await playwright.waitAndClick( - mainPageElements.connectedSites.disconnectLabel, - ); - await playwright.waitAndClick( - mainPageElements.connectedSites.disconnectButton, - ); - } else { - console.log( - '[disconnectWalletFromDapp] Wallet is not connected to a dapp, skipping..', - ); - } - await module.exports.closeModal(); - await switchToCypressIfNotActive(); - return true; - }, - async disconnectWalletFromAllDapps() { - await switchToPhantomIfNotActive(); - await playwright.waitAndClick(mainPageElements.optionsMenu.button); - await playwright.waitAndClick( - mainPageElements.optionsMenu.connectedSitesButton, - ); - const disconnectLabels = await playwright - .metamaskWindow() - .$$(mainPageElements.connectedSites.disconnectLabel); - if (disconnectLabels.length) { - console.log( - '[disconnectWalletFromAllDapps] Wallet is connected to dapps, disconnecting..', - ); - // eslint-disable-next-line no-unused-vars - for (const disconnectLabel of disconnectLabels) { - await playwright.waitAndClick( - mainPageElements.connectedSites.disconnectLabel, - ); - await playwright.waitAndClick( - mainPageElements.connectedSites.disconnectButton, - ); - } - } else { - console.log( - '[disconnectWalletFromAllDapps] Wallet is not connected to any dapps, skipping..', - ); - } - await module.exports.closeModal(); - await switchToCypressIfNotActive(); - return true; - }, - activateAdvancedGasControl: async skipSetup => { - return await activateAdvancedSetting( - advancedPageElements.advancedGasControlToggleOn, - advancedPageElements.advancedGasControlToggleOff, - skipSetup, - ); - }, - activateEnhancedTokenDetection: async skipSetup => { - return await activateAdvancedSetting( - advancedPageElements.enhancedTokenDetectionToggleOn, - advancedPageElements.enhancedTokenDetectionToggleOff, - skipSetup, - ); - }, - activateShowHexData: async skipSetup => { - return await activateAdvancedSetting( - advancedPageElements.showHexDataToggleOn, - advancedPageElements.showHexDataToggleOff, - skipSetup, - ); - }, - activateTestnetConversion: async skipSetup => { - return await activateAdvancedSetting( - advancedPageElements.showTestnetConversionOn, - advancedPageElements.showTestnetConversionOff, - skipSetup, - ); - }, - activateShowTestnetNetworks: async skipSetup => { - return await activateAdvancedSetting( - advancedPageElements.showTestnetNetworksOn, - advancedPageElements.showTestnetNetworksOff, - skipSetup, - ); - }, - activateCustomNonce: async skipSetup => { - return await activateAdvancedSetting( - advancedPageElements.customNonceToggleOn, - advancedPageElements.customNonceToggleOff, - skipSetup, - ); - }, - activateDismissBackupReminder: async skipSetup => { - return await activateAdvancedSetting( - advancedPageElements.dismissBackupReminderOn, - advancedPageElements.dismissBackupReminderOff, - skipSetup, - ); - }, - activateEnhancedGasFeeUI: async skipSetup => { - return await activateAdvancedSetting( - experimentalSettingsPageElements.enhancedGasFeeUIToggleOn, - experimentalSettingsPageElements.enhancedGasFeeUIToggleOff, - skipSetup, - true, - ); - }, - activateShowCustomNetworkList: async skipSetup => { - return await activateAdvancedSetting( - experimentalSettingsPageElements.showCustomNetworkListToggleOn, - experimentalSettingsPageElements.showCustomNetworkListToggleOff, - skipSetup, - true, - ); - }, - resetAccount: async () => { - await switchToPhantomIfNotActive(); - await module.exports.goToAdvancedSettings(); - await playwright.waitAndClick(advancedPageElements.resetAccountButton); - await playwright.waitAndClick(resetAccountModalElements.resetButton); - await playwright.waitAndClick( - settingsPageElements.closeButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); - await switchToCypressIfNotActive(); - return true; - }, - confirmSignatureRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - if ( - await playwright - .metamaskNotificationWindow() - .locator(signaturePageElements.signatureRequestScrollDownButton) - .isVisible() - ) { - await playwright.waitAndClick( - signaturePageElements.signatureRequestScrollDownButton, - notificationPage, - ); - } - await playwright.waitAndClick( - signaturePageElements.confirmSignatureRequestButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - rejectSignatureRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - signaturePageElements.rejectSignatureRequestButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - confirmDataSignatureRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - if ( + const popupBackground = playwright + .metamaskWindow() + .locator(mainPageElements.popup.background); + const popupBackgroundBox = await popupBackground.boundingBox(); await playwright - .metamaskNotificationWindow() - .locator(signaturePageElements.signatureRequestScrollDownButton) - .isVisible() - ) { - await playwright.waitAndClick( - signaturePageElements.signatureRequestScrollDownButton, - notificationPage, - ); - } - await playwright.waitAndClick( - dataSignaturePageElements.confirmDataSignatureRequestButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - rejectDataSignatureRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - dataSignaturePageElements.rejectDataSignatureRequestButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - importToken: async tokenConfig => { - let tokenData = {}; - await switchToPhantomIfNotActive(); - await module.exports.goToImportToken(); - if (typeof tokenConfig === 'string') { - await playwright.waitAndType( - mainPageElements.importToken.tokenContractAddressInput, - tokenConfig, - ); - tokenData.tokenContractAddress = tokenConfig; - tokenData.tokenSymbol = await playwright.waitAndGetInputValue( - mainPageElements.importToken.tokenSymbolInput, - ); - } else { - await playwright.waitAndType( - mainPageElements.importToken.tokenContractAddressInput, - tokenConfig.address, - ); - tokenData.tokenContractAddress = tokenConfig.address; - await playwright.waitAndClick( - mainPageElements.importToken.tokenEditButton, - await playwright.metamaskWindow(), - { - force: true, - }, - ); - await playwright.waitClearAndType( - tokenConfig.symbol, - mainPageElements.importToken.tokenSymbolInput, - ); - tokenData.tokenSymbol = tokenConfig.symbol; - } - tokenData.tokenDecimals = await playwright.waitAndGetInputValue( - mainPageElements.importToken.tokenDecimalInput, - ); - await playwright.waitAndClick( - mainPageElements.importToken.addCustomTokenButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await playwright.waitAndClick( - mainPageElements.importToken.importTokensButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await playwright.waitAndClick( - mainPageElements.asset.backButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); - await switchToCypressIfNotActive(); - tokenData.imported = true; - return tokenData; - }, - confirmAddToken: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - addTokenPageElements.confirmAddTokenButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - rejectAddToken: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - addTokenPageElements.rejectAddTokenButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - confirmPermissionToSpend: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - notificationPageElements.allowToSpendButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - rejectPermissionToSpend: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - notificationPageElements.rejectToSpendButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - acceptAccess: async options => { - const notificationPage = await playwright.switchToMetamaskNotification(); - if (options && options.allAccounts) { - await playwright.waitAndClick( - notificationPageElements.selectAllCheckbox, - notificationPage, - ); - } - await playwright.waitAndClick( - notificationPageElements.nextButton, - notificationPage, - { waitForEvent: 'navi' }, - ); - if (options && options.signInSignature) { - await playwright.waitAndClick( - permissionsPageElements.connectButton, - notificationPage, - { waitForEvent: 'navi' }, - ); - await module.exports.confirmSignatureRequest(); - } else { - await playwright.waitAndClick( - permissionsPageElements.connectButton, - notificationPage, - { waitForEvent: 'close' }, - ); - } - return true; - }, - confirmTransaction: async gasConfig => { - let txData = {}; - const notificationPage = await playwright.switchToMetamaskNotification(); - if (gasConfig) { - log( - '[confirmTransaction] gasConfig is present, determining transaction type..', - ); - if ( - await playwright - .metamaskNotificationWindow() - .locator(confirmPageElements.editGasFeeLegacyButton) - .isVisible() - ) { - log('[confirmTransaction] Looks like legacy tx'); - if (typeof gasConfig === 'object') { - log('[confirmTransaction] Editing legacy tx..'); - await playwright.waitAndClick( - confirmPageElements.editGasFeeLegacyButton, - notificationPage, - ); - if ( - await playwright - .metamaskNotificationWindow() - .locator(confirmPageElements.editGasFeeLegacyOverrideAckButton) - .isVisible() - ) { - log( - '[confirmTransaction] Override acknowledgement modal is present, closing..', - ); - await playwright.waitAndClick( - confirmPageElements.editGasFeeLegacyOverrideAckButton, - notificationPage, - ); - } - if (gasConfig.gasLimit) { - log('[confirmTransaction] Changing gas limit..'); - await playwright.waitAndSetValue( - gasConfig.gasLimit.toString(), - confirmPageElements.gasLimitLegacyInput, - notificationPage, - ); - } - if (gasConfig.gasPrice) { - log('[confirmTransaction] Changing gas price..'); - await playwright.waitAndSetValue( - gasConfig.gasPrice.toString(), - confirmPageElements.gasPriceLegacyInput, - notificationPage, - ); - } - await playwright.waitAndClick( - confirmPageElements.saveCustomGasFeeButton, - notificationPage, - ); - } else { - log( - "[confirmTransaction] Legacy tx doesn't support eip-1559 fees (low, market, aggressive, site), using default values..", - ); - } - } else { - log('[confirmTransaction] Looks like eip-1559 tx'); - await playwright.waitAndClick( - confirmPageElements.editGasFeeButton, - notificationPage, - ); - if (typeof gasConfig === 'string') { - if (gasConfig === 'low') { - log('[confirmTransaction] Changing gas fee to low..'); - await playwright.waitAndClick( - confirmPageElements.gasOptionLowButton, - notificationPage, - ); - } else if (gasConfig === 'market') { - log('[confirmTransaction] Changing gas fee to market..'); - await playwright.waitAndClick( - confirmPageElements.gasOptionMediumButton, - notificationPage, - ); - } else if (gasConfig === 'aggressive') { - log('[confirmTransaction] Changing gas fee to aggressive..'); - await playwright.waitAndClick( - confirmPageElements.gasOptionHighButton, - notificationPage, - ); - } else if (gasConfig === 'site') { - log('[confirmTransaction] Changing gas fee to site suggested..'); - await playwright.waitAndClick( - confirmPageElements.gasOptionDappSuggestedButton, - notificationPage, - ); - } - } else { - log('[confirmTransaction] Editing eip-1559 tx..'); - await playwright.waitAndClick( - confirmPageElements.gasOptionCustomButton, - notificationPage, - ); - if (gasConfig.gasLimit) { - log('[confirmTransaction] Changing gas limit..'); - await playwright.waitAndClick( - confirmPageElements.editGasLimitButton, - notificationPage, - ); - await playwright.waitAndSetValue( - gasConfig.gasLimit.toString(), - confirmPageElements.gasLimitInput, - notificationPage, - ); - } - if (gasConfig.baseFee) { - log('[confirmTransaction] Changing base fee..'); - await playwright.waitAndSetValue( - gasConfig.baseFee.toString(), - confirmPageElements.baseFeeInput, - notificationPage, - ); - } - if (gasConfig.priorityFee) { - log('[confirmTransaction] Changing priority fee..'); - await playwright.waitAndSetValue( - gasConfig.priorityFee.toString(), - confirmPageElements.priorityFeeInput, - notificationPage, - ); - } - await playwright.waitAndClick( - confirmPageElements.saveCustomGasFeeButton, - notificationPage, - ); - } - } + .metamaskWindow() + .mouse.click(popupBackgroundBox.x + 1, popupBackgroundBox.y + 1); } - log('[confirmTransaction] Checking if recipient address is present..'); if ( await playwright - .metamaskNotificationWindow() - .locator(confirmPageElements.recipientButton) + .metamaskWindow() + .locator(mainPageElements.tippyTooltip.closeButton) .isVisible() ) { - log('[confirmTransaction] Getting recipient address..'); - await playwright.waitAndClick( - confirmPageElements.recipientButton, - notificationPage, - ); - txData.recipientPublicAddress = await playwright.waitAndGetValue( - recipientPopupElements.recipientPublicAddress, - notificationPage, - ); - await playwright.waitAndClick( - recipientPopupElements.popupCloseButton, - notificationPage, - ); + await playwright.waitAndClick(mainPageElements.tippyTooltip.closeButton); } - log('[confirmTransaction] Checking if network name is present..'); if ( await playwright - .metamaskNotificationWindow() - .locator(confirmPageElements.networkLabel) + .metamaskWindow() + .locator(mainPageElements.actionableMessage.closeButton) .isVisible() ) { - log('[confirmTransaction] Getting network name..'); - txData.networkName = await playwright.waitAndGetValue( - confirmPageElements.networkLabel, - notificationPage, - ); - } - // todo: handle setting of custom nonce here - log('[confirmTransaction] Getting transaction nonce..'); - txData.customNonce = await playwright.waitAndGetAttributeValue( - confirmPageElements.customNonceInput, - 'placeholder', - notificationPage, - ); - // todo: fix getting tx data on function multicall - // log('[confirmTransaction] Checking if tx data is present..'); - // if ( - // await playwright - // .metamaskNotificationWindow() - // .locator(confirmPageElements.dataButton) - // .isVisible() - // ) { - // log('[confirmTransaction] Fetching tx data..'); - // await playwright.waitAndClick( - // confirmPageElements.dataButton, - // notificationPage, - // ); - // log('[confirmTransaction] Getting origin value..'); - // txData.origin = await playwright.waitAndGetValue( - // confirmPageElements.originValue, - // notificationPage, - // ); - // log('[confirmTransaction] Getting bytes value..'); - // txData.bytes = await playwright.waitAndGetValue( - // confirmPageElements.bytesValue, - // notificationPage, - // ); - // log('[confirmTransaction] Getting hex data value..'); - // txData.hexData = await playwright.waitAndGetValue( - // confirmPageElements.hexDataValue, - // notificationPage, - // ); - // await playwright.waitAndClick( - // confirmPageElements.detailsButton, - // notificationPage, - // ); - // } - log('[confirmTransaction] Confirming transaction..'); - await playwright.waitAndClick( - confirmPageElements.confirmButton, - notificationPage, - { waitForEvent: 'close' }, - ); - txData.confirmed = true; - log('[confirmTransaction] Transaction confirmed!'); - return txData; - }, - rejectTransaction: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - confirmPageElements.rejectButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - confirmEncryptionPublicKeyRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - encryptionPublicKeyPageElements.confirmEncryptionPublicKeyButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - rejectEncryptionPublicKeyRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - encryptionPublicKeyPageElements.rejectEncryptionPublicKeyButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - confirmDecryptionRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - decryptPageElements.confirmDecryptionRequestButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - rejectDecryptionRequest: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - decryptPageElements.rejectDecryptionRequestButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - allowToAddNetwork: async ({ waitForEvent } = {}) => { - const notificationPage = await playwright.switchToMetamaskNotification(); - if (waitForEvent) { await playwright.waitAndClick( - confirmationPageElements.footer.approveButton, - notificationPage, - { waitForEvent }, - ); - } else { - await playwright.waitAndClick( - confirmationPageElements.footer.approveButton, - notificationPage, + mainPageElements.actionableMessage.closeButton, ); } return true; }, - rejectToAddNetwork: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - confirmationPageElements.footer.cancelButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - allowToSwitchNetwork: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - confirmationPageElements.footer.approveButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - rejectToSwitchNetwork: async () => { - const notificationPage = await playwright.switchToMetamaskNotification(); - await playwright.waitAndClick( - confirmationPageElements.footer.cancelButton, - notificationPage, - { waitForEvent: 'close' }, - ); - return true; - }, - allowToAddAndSwitchNetwork: async () => { - await module.exports.allowToAddNetwork(); - await module.exports.allowToSwitchNetwork(); - return true; - }, getWalletAddress: async () => { await switchToPhantomIfNotActive(); await playwright.metamaskWindow().hover(mainPageElements.accountBar.title); @@ -1149,31 +257,23 @@ module.exports = { await module.exports.getExtensionDetails(); await module.exports.fixBlankPage(); - // password (unlock?) or new set up? if ( await playwright .metamaskWindow() .locator(firstTimeFlowPageElements.importWalletButton) .isVisible() ) { + /** + * SEED PHRASE IMPORT + */ if (secretWordsOrPrivateKey.includes(' ')) { // secret words await module.exports.importWallet(secretWordsOrPrivateKey, password); - } else { - // private key - await module.exports.createWallet(password); - await module.exports.importAccount(secretWordsOrPrivateKey); } - // await setupSettings(enableAdvancedSettings); - // if (isCustomNetwork) { - // await module.exports.addNetwork(network); - // } else { - // await module.exports.changeNetwork(network); - // } await switchToPhantomIfNotActive(); - // await module.exports.goToHome(); + // skip what's new header if ( await playwright .metamaskWindow() @@ -1187,6 +287,9 @@ module.exports = { await playwright.switchToCypressWindow(); return true; } else if ( + /** + * PASSWORD UNLOCK + */ await playwright .metamaskWindow() .locator(unlockPageElements.passwordInput) @@ -1213,6 +316,67 @@ module.exports = { } } }, + confirmSignatureRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + signaturePageElements.buttons.confirmSign, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + rejectSignatureRequest: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + signaturePageElements.buttons.rejectSign, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + confirmTransaction: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + transactionPageElements.buttons.rejectSign, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + confirmIncorrectNetworkStage: async () => { + const notificationPage = await playwright.switchToMetamaskNotification(); + await playwright.waitAndClick( + incorrectModePageElements.buttons.close, + notificationPage, + { waitForEvent: 'close' }, + ); + return true; + }, + unlock: async password => { + await playwright.waitAndType(unlockPageElements.passwordInput, password); + await playwright.waitAndClick( + unlockPageElements.unlockButton, + await playwright.metamaskWindow(), + ); + await module.exports.closePopupAndTooltips(); + return true; + }, + lock: async () => { + await playwright.waitAndClick( + menu.buttons.settings, + await playwright.metamaskWindow(), + ); + await playwright.waitAndClick( + menu.buttons.sidebar.settings, + await playwright.metamaskWindow(), + ); + await playwright.waitAndClick( + settingsPageElements.buttons.lockWallet, + await playwright.metamaskWindow(), + ); + await module.exports.closePopupAndTooltips(); + return true; + }, }; async function switchToPhantomIfNotActive() { @@ -1231,60 +395,3 @@ async function switchToCypressIfNotActive() { } return switchBackToCypressWindow; } - -async function activateAdvancedSetting( - toggleOn, - toggleOff, - skipSetup, - experimental, -) { - if (!skipSetup) { - await switchToPhantomIfNotActive(); - if (experimental) { - await module.exports.goToExperimentalSettings(); - } else { - await module.exports.goToAdvancedSettings(); - } - } - if (!(await playwright.metamaskWindow().locator(toggleOn).isVisible())) { - await playwright.waitAndClick(toggleOff); - } - if (!skipSetup) { - await playwright.waitAndClick( - settingsPageElements.closeButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); - await switchToCypressIfNotActive(); - } - return true; -} - -async function setupSettings(enableAdvancedSettings) { - await switchToPhantomIfNotActive(); - await module.exports.goToAdvancedSettings(); - await module.exports.activateAdvancedGasControl(true); - await module.exports.activateShowHexData(true); - await module.exports.activateShowTestnetNetworks(true); - await module.exports.activateCustomNonce(true); - await module.exports.activateDismissBackupReminder(true); - if (enableAdvancedSettings) { - await module.exports.activateEnhancedTokenDetection(true); - await module.exports.activateTestnetConversion(true); - } - await module.exports.goToExperimentalSettings(); - await module.exports.activateEnhancedGasFeeUI(true); - await playwright.waitAndClick( - settingsPageElements.closeButton, - await playwright.metamaskWindow(), - { - waitForEvent: 'navi', - }, - ); - await module.exports.closePopupAndTooltips(); - await switchToCypressIfNotActive(); - return true; -} diff --git a/commands/playwright.js b/commands/playwright.js index 8a8be8b..11c7e34 100644 --- a/commands/playwright.js +++ b/commands/playwright.js @@ -4,6 +4,7 @@ const { notificationPageElements, } = require('../pages/metamask/notification-page'); const { pageElements } = require('../pages/metamask/page'); +const { app } = require('../pages/phantom/notification-page'); const sleep = require('util').promisify(setTimeout); let browser; @@ -11,6 +12,8 @@ let mainWindow; let metamaskWindow; let metamaskNotificationWindow; let activeTabName; +const notificationWindowContainsText = + process.env.PROVIDER === 'phantom' ? 'notification' : 'notification'; let retries = 0; @@ -60,7 +63,7 @@ module.exports = { mainWindow = page; } else if (page.url().includes('extension')) { metamaskWindow = page; - } else if (page.url().includes('notification')) { + } else if (page.url().includes(notificationWindowContainsText)) { metamaskNotificationWindow = page; } } @@ -105,13 +108,21 @@ module.exports = { switchToMetamaskWindow: async () => { if (metamaskWindow.isClosed()) { const newPage = await browser.contexts()[0].newPage(); - await Promise.all([ - newPage.waitForNavigation(), - newPage.goto( - metamaskWindow.url().replace('onboarding.html', 'popup.html'), - ), - ]); - // await newPage.waitUntilStable(); + if (process.env.PROVIDER === 'phantom') { + await Promise.all([ + newPage.waitForNavigation(), + newPage.goto( + metamaskWindow.url().replace('onboarding.html', 'popup.html'), + ), + ]); + } else { + await Promise.all([ + newPage.waitForNavigation(), + newPage.goto(metamaskWindow.url()), + ]); + await newPage.waitUntilStable(); + } + metamaskWindow = newPage; } @@ -126,16 +137,24 @@ module.exports = { }, switchToMetamaskNotification: async () => { let pages = await browser.contexts()[0].pages(); - for (const page of pages) { - if (page.url().includes('notification')) { + + // loop reverse, chance is very high that it's the last window + for (let i = pages.length - 1; i >= 0; i--) { + const page = pages[i]; + if (page.url().includes(notificationWindowContainsText)) { metamaskNotificationWindow = page; retries = 0; await page.bringToFront(); await module.exports.waitUntilStable(page); - await module.exports.waitFor( - notificationPageElements.notificationAppContent, - page, - ); + if (process.env.PROVIDER === 'phantom') { + await module.exports.waitFor(app.root, page); + } else { + await module.exports.waitFor( + notificationPageElements.notificationAppContent, + page, + ); + } + return page; } } @@ -271,7 +290,7 @@ module.exports = { } }, waitUntilStable: async page => { - if (page && page.url().includes('notification')) { + if (page && page.url().includes(notificationWindowContainsText)) { await page.waitForLoadState('load'); await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('networkidle'); diff --git a/commands/synthetix.js b/commands/synthetix.js index 84ef1e9..bb9d65d 100644 --- a/commands/synthetix.js +++ b/commands/synthetix.js @@ -1,7 +1,10 @@ const { SynthetixJs } = require('synthetix-js'); const { synthetix } = require('@synthetixio/js'); const { getNetwork } = require('../helpers'); -const metamask = require('./phantom'); +const metamask = + process.env.PROVIDER === 'phantom' + ? require('./phantom') + : require('./metamask'); const bytes32 = require('bytes32'); const sleep = require('util').promisify(setTimeout); diff --git a/pages/phantom/notification-page.js b/pages/phantom/notification-page.js index 7844b44..2d72c23 100644 --- a/pages/phantom/notification-page.js +++ b/pages/phantom/notification-page.js @@ -144,3 +144,48 @@ module.exports.addTokenPageElements = { confirmAddTokenButton, rejectAddTokenButton, }; + +/** + * ADJUSTED + */ +const root = '#root'; +module.exports.app = { + root, +}; + +const primaryButton = '[data-testid="primary-button"]'; +module.exports.buttons = { + primaryButton, +}; + +/** + * NEW + */ +module.exports.signaturePageElements = { + buttons: { + confirmSign: '[data-testid="primary-button"]', + rejectSign: '[data-testid="secondary-button"]', + }, +}; + +module.exports.transactionPageElements = { + buttons: { + confirmSign: '[data-testid="primary-button"]', + rejectSign: '[data-testid="secondary-button"]', + }, +}; + +module.exports.menu = { + buttons: { + settings: '[data-testid="settings-menu-open-button"]', + sidebar: { + settings: '[data-testid="sidebar_menu-button-settings"]', + }, + }, +}; + +module.exports.incorrectModePageElements = { + buttons: { + close: '[data-testid="incorrect-mode"] button', + }, +}; diff --git a/pages/phantom/settings-page.js b/pages/phantom/settings-page.js index bcaee5b..79cfe29 100644 --- a/pages/phantom/settings-page.js +++ b/pages/phantom/settings-page.js @@ -1,94 +1,5 @@ -const settingsPage = '.settings-page'; -const advancedButton = `${settingsPage} button:nth-child(2)`; -const networksButton = `${settingsPage} button:nth-child(6)`; -const closeButton = `${settingsPage} .settings-page__header__title-container__close-button`; module.exports.settingsPageElements = { - settingsPage, - advancedButton, - networksButton, - closeButton, -}; - -const resetAccountButton = - '[data-testid="advanced-setting-reset-account"] button'; -const advancedGasControlToggleOn = - '[data-testid="advanced-setting-advanced-gas-inline"] .toggle-button--on'; -const advancedGasControlToggleOff = - '[data-testid="advanced-setting-advanced-gas-inline"] .toggle-button--off'; -const enhancedTokenDetectionToggleOn = - '[data-testid="advanced-setting-token-detection"] .toggle-button--on'; -const enhancedTokenDetectionToggleOff = - '[data-testid="advanced-setting-token-detection"] .toggle-button--off'; -const showHexDataToggleOn = - '[data-testid="advanced-setting-hex-data"] .toggle-button--on'; -const showHexDataToggleOff = - '[data-testid="advanced-setting-hex-data"] .toggle-button--off'; -const showTestnetConversionOn = - '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(7) .toggle-button--on'; -const showTestnetConversionOff = - '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(7) .toggle-button--off'; -const showTestnetNetworksOn = - '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(8) .toggle-button--on'; -const showTestnetNetworksOff = - '[data-testid="advanced-setting-show-testnet-conversion"]:nth-child(8) .toggle-button--off'; -const customNonceToggleOn = - '[data-testid="advanced-setting-custom-nonce"] .toggle-button--on'; -const customNonceToggleOff = - '[data-testid="advanced-setting-custom-nonce"] .toggle-button--off'; -const dismissBackupReminderOn = - '[data-testid="advanced-setting-dismiss-reminder"] .toggle-button--on'; -const dismissBackupReminderOff = - '[data-testid="advanced-setting-dismiss-reminder"] .toggle-button--off'; -module.exports.advancedPageElements = { - resetAccountButton, - advancedGasControlToggleOn, - advancedGasControlToggleOff, - enhancedTokenDetectionToggleOn, - enhancedTokenDetectionToggleOff, - showHexDataToggleOn, - showHexDataToggleOff, - showTestnetConversionOn, - showTestnetConversionOff, - showTestnetNetworksOn, - showTestnetNetworksOff, - dismissBackupReminderOn, - dismissBackupReminderOff, - customNonceToggleOn, - customNonceToggleOff, -}; - -const enhancedGasFeeUIToggleOn = - '.settings-page__content-row:nth-child(1) .toggle-button--on'; -const enhancedGasFeeUIToggleOff = - '.settings-page__content-row:nth-child(1) .toggle-button--off'; -module.exports.experimentalSettingsPageElements = { - enhancedGasFeeUIToggleOn, - enhancedGasFeeUIToggleOff, -}; - -const nevermindButton = '.modal-container .btn-secondary'; -const resetButton = '.modal-container .btn-danger-primary'; -module.exports.resetAccountModalElements = { - nevermindButton, - resetButton, -}; - -const addNetworkButton = '.networks-tab__body button'; -module.exports.networksPageElements = { addNetworkButton }; - -const addNetworkForm = '.networks-tab__add-network-form-body'; -const networkNameInput = `${addNetworkForm} .form-field:nth-child(1) input`; -const rpcUrlInput = `${addNetworkForm} .form-field:nth-child(2) input`; -const chainIdInput = `${addNetworkForm} .form-field:nth-child(3) input`; -const symbolInput = `${addNetworkForm} .form-field:nth-child(4) input`; -const blockExplorerInput = `${addNetworkForm} .form-field:nth-child(5) input`; -const saveButton = '.networks-tab__add-network-form-footer .btn-primary'; -module.exports.addNetworkPageElements = { - addNetworkForm, - networkNameInput, - rpcUrlInput, - chainIdInput, - symbolInput, - blockExplorerInput, - saveButton, + buttons: { + lockWallet: '[data-testid="lock-menu-item"]', + }, }; diff --git a/pages/phantom/unlock-page.js b/pages/phantom/unlock-page.js index 69e65f4..5665ea7 100644 --- a/pages/phantom/unlock-page.js +++ b/pages/phantom/unlock-page.js @@ -1,9 +1,4 @@ -const unlockPage = '[data-testid="unlock-page"]'; -const passwordInput = `${unlockPage} #password`; -const unlockButton = `${unlockPage} .btn-default`; - module.exports.unlockPageElements = { - unlockPage, - passwordInput, - unlockButton, + passwordInput: '[data-testid="unlock-form-password-input"]', + unlockButton: '[data-testid="unlock-form-submit-button"]', }; diff --git a/plugins/index.js b/plugins/index.js index a6c02b1..dea1c30 100644 --- a/plugins/index.js +++ b/plugins/index.js @@ -1,6 +1,9 @@ const helpers = require('../helpers'); const playwright = require('../commands/playwright'); -const metamask = require('../commands/phantom'); +const provider = + process.env.PROVIDER === 'phantom' + ? require('../commands/phantom') + : require('../commands/metamask'); const synthetix = require('../commands/synthetix'); const etherscan = require('../commands/etherscan'); @@ -27,6 +30,7 @@ module.exports = (on, config) => { // metamask welcome screen blocks cypress from loading if (browser.name === 'chrome') { + arguments_.args.push('--window-size=1920,1080'); // optional arguments_.args.push( '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', @@ -52,9 +56,13 @@ module.exports = (on, config) => { const metamaskPath = await helpers.prepareMetamask( process.env.METAMASK_VERSION || '10.21.0', ); - arguments_.extensions.push( - metamaskPath.replace('metamask-chrome-10.21.0', 'phantom'), - ); + if (process.env.PROVIDER === 'phantom') { + arguments_.extensions.push( + metamaskPath.replace('metamask-chrome-10.21.0', 'phantom'), + ); + } else { + arguments_.extensions.push(metamaskPath); + } } return arguments_; @@ -110,24 +118,36 @@ module.exports = (on, config) => { const notificationPage = await playwright.switchToMetamaskNotification(); return notificationPage; }, + /** + * @deprecated + */ unlockMetamask: async password => { - const unlocked = await metamask.unlock(password); + return module.exports.unlock(password); + }, + unlock: async password => { + const unlocked = await provider.unlock(password); return unlocked; }, + lock: () => { + return provider.lock(); + }, + confirmIncorrectNetworkStage: () => { + return provider.confirmIncorrectNetworkStage(); + }, importMetamaskAccount: async privateKey => { - const imported = await metamask.importAccount(privateKey); + const imported = await provider.importAccount(privateKey); return imported; }, createMetamaskAccount: async accountName => { - const created = await metamask.createAccount(accountName); + const created = await provider.createAccount(accountName); return created; }, switchMetamaskAccount: async accountNameOrAccountNumber => { - const switched = await metamask.switchAccount(accountNameOrAccountNumber); + const switched = await provider.switchAccount(accountNameOrAccountNumber); return switched; }, addMetamaskNetwork: async network => { - const networkAdded = await metamask.addNetwork(network); + const networkAdded = await provider.addNetwork(network); return networkAdded; }, changeMetamaskNetwork: async network => { @@ -136,149 +156,178 @@ module.exports = (on, config) => { } else if (!network) { network = 'goerli'; } - const networkChanged = await metamask.changeNetwork(network); + const networkChanged = await provider.changeNetwork(network); return networkChanged; }, activateAdvancedGasControlInMetamask: async skipSetup => { - const activated = await metamask.activateAdvancedGasControl(skipSetup); + const activated = await provider.activateAdvancedGasControl(skipSetup); return activated; }, activateEnhancedTokenDetectionInMetamask: async skipSetup => { - const activated = await metamask.activateEnhancedTokenDetection( + const activated = await provider.activateEnhancedTokenDetection( skipSetup, ); return activated; }, activateShowHexDataInMetamask: async skipSetup => { - const activated = await metamask.activateShowHexData(skipSetup); + const activated = await provider.activateShowHexData(skipSetup); return activated; }, activateTestnetConversionInMetamask: async skipSetup => { - const activated = await metamask.activateTestnetConversion(skipSetup); + const activated = await provider.activateTestnetConversion(skipSetup); return activated; }, activateShowTestnetNetworksInMetamask: async skipSetup => { - const activated = await metamask.activateShowTestnetNetworks(skipSetup); + const activated = await provider.activateShowTestnetNetworks(skipSetup); return activated; }, activateCustomNonceInMetamask: async skipSetup => { - const activated = await metamask.activateCustomNonce(skipSetup); + const activated = await provider.activateCustomNonce(skipSetup); return activated; }, activateDismissBackupReminderInMetamask: async skipSetup => { - const activated = await metamask.activateDismissBackupReminder(skipSetup); + const activated = await provider.activateDismissBackupReminder(skipSetup); return activated; }, activateEnhancedGasFeeUIInMetamask: async skipSetup => { - const activated = await metamask.activateEnhancedGasFeeUI(skipSetup); + const activated = await provider.activateEnhancedGasFeeUI(skipSetup); return activated; }, activateShowCustomNetworkListInMetamask: async skipSetup => { - const activated = await metamask.activateShowCustomNetworkList(skipSetup); + const activated = await provider.activateShowCustomNetworkList(skipSetup); return activated; }, resetMetamaskAccount: async () => { - const resetted = await metamask.resetAccount(); + const resetted = await provider.resetAccount(); return resetted; }, disconnectMetamaskWalletFromDapp: async () => { - const disconnected = await metamask.disconnectWalletFromDapp(); + const disconnected = await provider.disconnectWalletFromDapp(); return disconnected; }, disconnectMetamaskWalletFromAllDapps: async () => { - const disconnected = await metamask.disconnectWalletFromAllDapps(); + const disconnected = await provider.disconnectWalletFromAllDapps(); return disconnected; }, + + /** + * @deprecated + */ confirmMetamaskSignatureRequest: async () => { - const confirmed = await metamask.confirmSignatureRequest(); + return module.exports.confirmSignatureRequest(); + }, + confirmSignatureRequest: async () => { + const confirmed = await provider.confirmSignatureRequest(); return confirmed; }, + confirmMetamaskDataSignatureRequest: async () => { - const confirmed = await metamask.confirmDataSignatureRequest(); + const confirmed = await provider.confirmDataSignatureRequest(); return confirmed; }, rejectMetamaskSignatureRequest: async () => { - const rejected = await metamask.rejectSignatureRequest(); + const rejected = await provider.rejectSignatureRequest(); return rejected; }, rejectMetamaskDataSignatureRequest: async () => { - const rejected = await metamask.rejectDataSignatureRequest(); + const rejected = await provider.rejectDataSignatureRequest(); return rejected; }, confirmMetamaskEncryptionPublicKeyRequest: async () => { - const confirmed = await metamask.confirmEncryptionPublicKeyRequest(); + const confirmed = await provider.confirmEncryptionPublicKeyRequest(); return confirmed; }, rejectMetamaskEncryptionPublicKeyRequest: async () => { - const rejected = await metamask.rejectEncryptionPublicKeyRequest(); + const rejected = await provider.rejectEncryptionPublicKeyRequest(); return rejected; }, confirmMetamaskDecryptionRequest: async () => { - const confirmed = await metamask.confirmDecryptionRequest(); + const confirmed = await provider.confirmDecryptionRequest(); return confirmed; }, rejectMetamaskDecryptionRequest: async () => { - const rejected = await metamask.rejectDecryptionRequest(); + const rejected = await provider.rejectDecryptionRequest(); return rejected; }, importMetamaskToken: async tokenConfig => { - const imported = await metamask.importToken(tokenConfig); + const imported = await provider.importToken(tokenConfig); return imported; }, confirmMetamaskAddToken: async () => { - const confirmed = await metamask.confirmAddToken(); + const confirmed = await provider.confirmAddToken(); return confirmed; }, rejectMetamaskAddToken: async () => { - const rejected = await metamask.rejectAddToken(); + const rejected = await provider.rejectAddToken(); return rejected; }, confirmMetamaskPermissionToSpend: async () => { - const confirmed = await metamask.confirmPermissionToSpend(); + const confirmed = await provider.confirmPermissionToSpend(); return confirmed; }, rejectMetamaskPermissionToSpend: async () => { - const rejected = await metamask.rejectPermissionToSpend(); + const rejected = await provider.rejectPermissionToSpend(); return rejected; }, + + /** + * @deprecated + */ acceptMetamaskAccess: async options => { - const accepted = await metamask.acceptAccess(options); + return module.exports.acceptAccess(options); + }, + acceptAccess: async options => { + const accepted = await provider.acceptAccess(options); return accepted; }, + + /** + * @deprecated + */ confirmMetamaskTransaction: async gasConfig => { - const confirmed = await metamask.confirmTransaction(gasConfig); + return module.exports.confirmTransaction(gasConfig); + }, + confirmTransaction: async gasConfig => { + const confirmed = await provider.confirmTransaction(gasConfig); return confirmed; }, + rejectMetamaskTransaction: async () => { - const rejected = await metamask.rejectTransaction(); + const rejected = await provider.rejectTransaction(); return rejected; }, allowMetamaskToAddNetwork: async ({ waitForEvent }) => { - const allowed = await metamask.allowToAddNetwork({ waitForEvent }); + const allowed = await provider.allowToAddNetwork({ waitForEvent }); return allowed; }, rejectMetamaskToAddNetwork: async () => { - const rejected = await metamask.rejectToAddNetwork(); + const rejected = await provider.rejectToAddNetwork(); return rejected; }, allowMetamaskToSwitchNetwork: async () => { - const allowed = await metamask.allowToSwitchNetwork(); + const allowed = await provider.allowToSwitchNetwork(); return allowed; }, rejectMetamaskToSwitchNetwork: async () => { - const rejected = await metamask.rejectToSwitchNetwork(); + const rejected = await provider.rejectToSwitchNetwork(); return rejected; }, allowMetamaskToAddAndSwitchNetwork: async () => { - const allowed = await metamask.allowToAddAndSwitchNetwork(); + const allowed = await provider.allowToAddAndSwitchNetwork(); return allowed; }, getMetamaskWalletAddress: async () => { - const walletAddress = await metamask.getWalletAddress(); + const walletAddress = await provider.getWalletAddress(); return walletAddress; }, + /** + * @deprecated + */ fetchMetamaskWalletAddress: async () => { - return metamask.walletAddress(); + return module.exports.fetchWalletAddress(); + }, + fetchWalletAddress: async () => { + return provider.walletAddress(); }, setupMetamask: async ({ secretWordsOrPrivateKey, @@ -295,7 +344,7 @@ module.exports = (on, config) => { if (process.env.SECRET_WORDS) { secretWordsOrPrivateKey = process.env.SECRET_WORDS; } - await metamask.initialSetup({ + await provider.initialSetup({ secretWordsOrPrivateKey, network, password, diff --git a/support/commands.js b/support/commands.js index 0d83999..42d93de 100644 --- a/support/commands.js +++ b/support/commands.js @@ -2,6 +2,23 @@ import '@testing-library/cypress/add-commands'; import 'cypress-wait-until'; // playwright commands +const addCommand = (commandName, legacyCommandName, promiseFn) => { + Cypress.Commands.add(commandName, parameters => { + if (promiseFn) { + return promiseFn(cy.task(commandName, parameters)); + } + return cy.task(commandName, parameters); + }); + + if (legacyCommandName != null) { + Cypress.Commands.add(legacyCommandName, parameters => { + if (promiseFn) { + return promiseFn(cy.task(commandName, parameters)); + } + return cy.task(legacyCommandName, parameters); + }); + } +}; Cypress.Commands.add('initPlaywright', () => { return cy.task('initPlaywright'); @@ -132,9 +149,7 @@ Cypress.Commands.add('disconnectMetamaskWalletFromAllDapps', () => { return cy.task('disconnectMetamaskWalletFromAllDapps'); }); -Cypress.Commands.add('confirmMetamaskSignatureRequest', () => { - return cy.task('confirmMetamaskSignatureRequest'); -}); +addCommand('confirmSignatureRequest', 'confirmMetamaskSignatureRequest'); Cypress.Commands.add('confirmMetamaskEncryptionPublicKeyRequest', () => { return cy.task('confirmMetamaskEncryptionPublicKeyRequest'); @@ -184,13 +199,8 @@ Cypress.Commands.add('rejectMetamaskPermissionToSpend', () => { return cy.task('rejectMetamaskPermissionToSpend'); }); -Cypress.Commands.add('acceptMetamaskAccess', options => { - return cy.task('acceptMetamaskAccess', options); -}); - -Cypress.Commands.add('confirmMetamaskTransaction', gasConfig => { - return cy.task('confirmMetamaskTransaction', gasConfig); -}); +addCommand('acceptAccess', 'acceptMetamaskAccess'); +addCommand('confirmTransaction', 'confirmMetamaskTransaction'); Cypress.Commands.add('rejectMetamaskTransaction', () => { return cy.task('rejectMetamaskTransaction'); @@ -216,16 +226,24 @@ Cypress.Commands.add('allowMetamaskToAddAndSwitchNetwork', () => { return cy.task('allowMetamaskToAddAndSwitchNetwork'); }); +/** + * @deprecated + */ Cypress.Commands.add('unlockMetamask', (password = 'Tester@1234') => { return cy.task('unlockMetamask', password); }); - -Cypress.Commands.add('fetchMetamaskWalletAddress', () => { - cy.task('fetchMetamaskWalletAddress').then(address => { - return address; - }); +Cypress.Commands.add('unlock', (password = 'Tester@1234') => { + return cy.task('unlock', password); }); +addCommand('lock'); + +addCommand('fetchWalletAddress', 'fetchMetamaskWalletAddress', taskPromise => + taskPromise.then(address => address), +); + +addCommand('confirmIncorrectNetworkStage'); + Cypress.Commands.add( 'setupMetamask', ( @@ -243,6 +261,23 @@ Cypress.Commands.add( }, ); +Cypress.Commands.add( + 'setup', + ( + secretWordsOrPrivateKey = 'test test test test test test test test test test test junk', + network = 'goerli', + password = 'Tester@1234', + enableAdvancedSettings = false, + ) => { + return cy.task('setup', { + secretWordsOrPrivateKey, + network, + password, + enableAdvancedSettings, + }); + }, +); + Cypress.Commands.add('getNetwork', () => { return cy.task('getNetwork'); }); From a8383447c39983aea1a6bb43e168d8d3d4bee399 Mon Sep 17 00:00:00 2001 From: Maxim Geerinck Date: Thu, 9 Feb 2023 16:45:24 +0100 Subject: [PATCH 4/5] chore: Adjust timings --- commands/phantom.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/commands/phantom.js b/commands/phantom.js index c5d710b..249a1dd 100644 --- a/commands/phantom.js +++ b/commands/phantom.js @@ -176,18 +176,15 @@ module.exports = { firstTimeFlowImportPageElements.continueAfterPasswordButton, ); // shortcut confirmation - await new Promise(resolve => setTimeout(resolve, 1000)); // the transitioning is too fast + await new Promise(resolve => setTimeout(resolve, 500)); // the transitioning is too fast await playwright.waitAndClick( firstTimeFlowImportPageElements.continueOnShortcutConfirm, ); // finish - await new Promise(resolve => setTimeout(resolve, 1000)); // the transitioning is too fast + await new Promise(resolve => setTimeout(resolve, 500)); // the transitioning is too fast await playwright.waitAndClick( firstTimeFlowImportPageElements.continueOnShortcutConfirm, ); - - await new Promise(resolve => setTimeout(resolve, 1000)); // the transitioning is too fast - // await module.exports.closePopupAndTooltips(); return true; }, closePopupAndTooltips: async () => { @@ -231,7 +228,7 @@ module.exports = { getWalletAddress: async () => { await switchToPhantomIfNotActive(); await playwright.metamaskWindow().hover(mainPageElements.accountBar.title); - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise(resolve => setTimeout(resolve, 100)); await playwright.waitAndClick(mainPageElements.accountBar.ethRow); walletAddress = await playwright .metamaskWindow() From 874aad7a907800a71391838aa292327ef2710c6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 16:29:12 +0000 Subject: [PATCH 5/5] chore(deps): Bump loader-utils from 1.4.0 to 1.4.2 Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.2. - [Release notes](https://github.com/webpack/loader-utils/releases) - [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.2/CHANGELOG.md) - [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.2) --- updated-dependencies: - dependency-name: loader-utils dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/yarn.lock b/yarn.lock index dd3f4bc..ebdac80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8798,9 +8798,9 @@ json5@^0.5.1: integrity sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw== json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" @@ -9045,9 +9045,9 @@ load-json-file@^1.0.0: strip-bom "^2.0.0" loader-utils@^1.2.3: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + version "1.4.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" + integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" @@ -9524,16 +9524,16 @@ minimatch@^5.1.2: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.6, minimist@~1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -minimist@^1.2.7: +minimist@^1.2.0, minimist@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.6, minimist@~1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"