diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index b77773a0a..6800c5104 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,5 +6,4 @@ export * from './createCache' export * from './defineWalletSetup' export * from './utils/createTempContextDir' export * from './utils/removeTempContextDir' -export * from './utils/waitForExtensionOnLoadPage' export * from './prepareExtension' diff --git a/packages/core/src/prepareExtension.ts b/packages/core/src/prepareExtension.ts index 3a2eaec30..1c041ae24 100644 --- a/packages/core/src/prepareExtension.ts +++ b/packages/core/src/prepareExtension.ts @@ -1,6 +1,6 @@ import { downloadFile, ensureCacheDirExists, unzipArchive } from '.' -export const DEFAULT_METAMASK_VERSION = '10.25.0' +export const DEFAULT_METAMASK_VERSION = '11.5.1' export const EXTENSION_DOWNLOAD_URL = `https://github.com/MetaMask/metamask-extension/releases/download/v${DEFAULT_METAMASK_VERSION}/metamask-chrome-${DEFAULT_METAMASK_VERSION}.zip` // NOTE: This function is copied from `wallets/metamask/src/prepareExtension.ts` only TEMPORARILY! diff --git a/packages/fixtures/package.json b/packages/fixtures/package.json index bfe8c53d5..eb89b28e7 100644 --- a/packages/fixtures/package.json +++ b/packages/fixtures/package.json @@ -19,6 +19,7 @@ "scripts": { "build": "pnpm run clean && pnpm run build:dist && pnpm run build:types", "build:dist": "tsup", + "build:dist:watch": "tsup --watch", "build:types": "tsc --emitDeclarationOnly", "clean": "rimraf dist types", "lint": "biome check . --apply", diff --git a/packages/fixtures/src/fixtures/testWithSynpress.ts b/packages/fixtures/src/fixtures/testWithSynpress.ts index 520b315c5..6fd0d85fb 100644 --- a/packages/fixtures/src/fixtures/testWithSynpress.ts +++ b/packages/fixtures/src/fixtures/testWithSynpress.ts @@ -8,7 +8,7 @@ import type { PlaywrightWorkerOptions } from '@playwright/test' import { chromium, test as base } from '@playwright/test' -import { defineWalletSetup, waitForExtensionOnLoadPage } from 'core' +import { defineWalletSetup } from 'core' import { createTempContextDir, removeTempContextDir } from 'core' import { CACHE_DIR_NAME, prepareExtension } from 'core' import fs from 'fs-extra' @@ -77,7 +77,16 @@ const synpressFixtures = ( slowMo: process.env.HEADLESS ? 0 : slowMo }) - _metamaskPage = await waitForExtensionOnLoadPage(context) + // TODO: This should be stored in a store to speed up the tests. + const extensionId = await getExtensionId(context, 'MetaMask') + + // TODO: Not sure if this is the best approach. Time will tell. + // We're utilizing the blank page here. + _metamaskPage = context.pages()[0] as Page + + await _metamaskPage.goto(`chrome-extension://${extensionId}/home.html`) + + await _metamaskPage.reload() await unlockWallet(_metamaskPage, walletSetup.walletPassword) diff --git a/wallets/metamask/src/fixture-actions/unlockForFixture.ts b/wallets/metamask/src/fixture-actions/unlockForFixture.ts index 0a9822dc8..47eaeaaa2 100644 --- a/wallets/metamask/src/fixture-actions/unlockForFixture.ts +++ b/wallets/metamask/src/fixture-actions/unlockForFixture.ts @@ -2,34 +2,29 @@ import type { Page } from '@playwright/test' import { errors as playwrightErrors } from '@playwright/test' import { MetaMask } from '../metamask' import { CrashPage, HomePage } from '../pages' -import { LoadingSelectors } from '../selectors' -import { waitFor } from '../utils/waitFor' +import { closePopover, closeRecoveryPhraseReminder } from '../pages/HomePage/actions' +import { waitForSpinnerToVanish } from '../utils/waitForSpinnerToVanish' export async function unlockForFixture(page: Page, password: string) { const metamask = new MetaMask(page.context(), page, password) await metamask.unlock() - await page.locator(LoadingSelectors.spinner).waitFor({ - state: 'hidden', - timeout: 3000 // TODO: Extract & Make this timeout configurable. - }) + await waitForSpinnerToVanish(page) await retryIfMetaMaskCrashAfterUnlock(page) - const recoveryPhraseReminder = page.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton) - - // TODO: Extract & Make this timeout configurable. - const isRecoveryPhraseReminderVisible = await waitFor(() => recoveryPhraseReminder.isVisible(), 1000, false) - if (isRecoveryPhraseReminderVisible) { - await recoveryPhraseReminder.click() - } + await closePopover(page) + await closeRecoveryPhraseReminder(page) } async function retryIfMetaMaskCrashAfterUnlock(page: Page) { - const isHomePageVisible = await page.locator(HomePage.selectors.logo).isVisible() + const homePageLogoLocator = page.locator(HomePage.selectors.logo) + + const isHomePageLogoVisible = await homePageLogoLocator.isVisible() + const isPopoverVisible = await page.locator(HomePage.selectors.popover.closeButton).isVisible() - if (!isHomePageVisible) { + if (!isHomePageLogoVisible && !isPopoverVisible) { if (await page.locator(CrashPage.selectors.header).isVisible()) { const errors = await page.locator(CrashPage.selectors.errors).allTextContents() @@ -39,7 +34,7 @@ async function retryIfMetaMaskCrashAfterUnlock(page: Page) { await page.reload() try { - await page.locator(HomePage.selectors.logo).waitFor({ + await homePageLogoLocator.waitFor({ state: 'visible', timeout: 10_000 // TODO: Extract & Make this timeout configurable. }) diff --git a/wallets/metamask/src/metamask.ts b/wallets/metamask/src/metamask.ts index 5c3c0e3fa..746c9f0a9 100644 --- a/wallets/metamask/src/metamask.ts +++ b/wallets/metamask/src/metamask.ts @@ -126,7 +126,7 @@ export class MetaMask { await this.notificationPage.rejectTransaction(this.extensionId) } - async approvePermission(spendLimit?: 'default' | 'max' | number) { + async approvePermission(spendLimit?: 'max' | number) { if (!this.extensionId) { throw NO_EXTENSION_ID_ERROR } @@ -158,8 +158,8 @@ export class MetaMask { await this.homePage.toggleShowTestNetworks() } - async toggleImprovedTokenAllowanceExperience() { - await this.homePage.toggleImprovedTokenAllowanceExperience() + async toggleDismissSecretRecoveryPhraseReminder() { + await this.homePage.toggleDismissSecretRecoveryPhraseReminder() } // ---- EXPERIMENTAL FEATURES ---- diff --git a/wallets/metamask/src/pages/HomePage/actions/importWalletFromPrivateKey.ts b/wallets/metamask/src/pages/HomePage/actions/importWalletFromPrivateKey.ts index ce7a4ca00..85a34ea97 100644 --- a/wallets/metamask/src/pages/HomePage/actions/importWalletFromPrivateKey.ts +++ b/wallets/metamask/src/pages/HomePage/actions/importWalletFromPrivateKey.ts @@ -3,19 +3,22 @@ import { waitFor } from '../../../utils/waitFor' import Selectors from '../selectors' export async function importWalletFromPrivateKey(page: Page, privateKey: string) { - await page.locator(Selectors.accountMenu.accountMenuButton).click() - await page.locator(Selectors.accountMenu.importAccountButton).click() - await page.locator(Selectors.importAccountScreen.privateKeyInput).fill(privateKey) + await page.locator(Selectors.accountMenu.accountButton).click() - const importButton = page.locator(Selectors.importAccountScreen.importButton) + await page.locator(Selectors.accountMenu.addAccountMenu.addAccountButton).click() + await page.locator(Selectors.accountMenu.addAccountMenu.importAccountButton).click() + + await page.locator(Selectors.accountMenu.addAccountMenu.importAccountMenu.privateKeyInput).fill(privateKey) + + const importButton = page.locator(Selectors.accountMenu.addAccountMenu.importAccountMenu.importButton) await importButton.click() // TODO: Extract & make configurable - const isHidden = await waitFor(() => importButton.isHidden(), 1000, false) + const isImportButtonHidden = await waitFor(() => importButton.isHidden(), 1_000, false) - if (!isHidden) { - const errorText = await page.locator(Selectors.importAccountScreen.error).textContent({ - timeout: 1000 // TODO: Extract & make configurable + if (!isImportButtonHidden) { + const errorText = await page.locator(Selectors.accountMenu.addAccountMenu.importAccountMenu.error).textContent({ + timeout: 1_000 // TODO: Extract & make configurable }) throw new Error(`[ImportWalletFromPrivateKey] Importing failed due to error: ${errorText}`) diff --git a/wallets/metamask/src/pages/HomePage/actions/index.ts b/wallets/metamask/src/pages/HomePage/actions/index.ts index b1f6cd3ea..83c7eab63 100644 --- a/wallets/metamask/src/pages/HomePage/actions/index.ts +++ b/wallets/metamask/src/pages/HomePage/actions/index.ts @@ -1,6 +1,8 @@ +export * from './popups' export * from './lock' export * from './importWalletFromPrivateKey' export * from './switchAccount' export * from './settings' export * from './switchNetwork' export * from './addNetwork' +export * from './toggleShowTestNetworks' diff --git a/wallets/metamask/src/pages/HomePage/actions/lock.ts b/wallets/metamask/src/pages/HomePage/actions/lock.ts index f6e109820..b6218cd10 100644 --- a/wallets/metamask/src/pages/HomePage/actions/lock.ts +++ b/wallets/metamask/src/pages/HomePage/actions/lock.ts @@ -2,6 +2,6 @@ import type { Page } from '@playwright/test' import Selectors from '../selectors' export async function lock(page: Page) { - await page.locator(Selectors.accountMenu.accountMenuButton).click() - await page.locator(Selectors.accountMenu.lockButton).click() + await page.locator(Selectors.threeDotsMenu.threeDotsButton).click() + await page.locator(Selectors.threeDotsMenu.lockButton).click() } diff --git a/wallets/metamask/src/pages/HomePage/actions/popups/closePopover.ts b/wallets/metamask/src/pages/HomePage/actions/popups/closePopover.ts new file mode 100644 index 000000000..d0113a430 --- /dev/null +++ b/wallets/metamask/src/pages/HomePage/actions/popups/closePopover.ts @@ -0,0 +1,11 @@ +import type { Page } from '@playwright/test' +import { clickLocatorIfCondition } from '../../../../utils/closePopup' +import Selectors from '../../selectors' + +// Closes the popover with news, rainbows, unicorns, and other stuff. +export async function closePopover(page: Page) { + const closeButtonLocator = page.locator(Selectors.popover.closeButton) + + // TODO: Extract & make configurable + await clickLocatorIfCondition(closeButtonLocator, () => closeButtonLocator.isVisible(), 1_000) +} diff --git a/wallets/metamask/src/pages/HomePage/actions/popups/closeRecoveryPhraseReminder.ts b/wallets/metamask/src/pages/HomePage/actions/popups/closeRecoveryPhraseReminder.ts new file mode 100644 index 000000000..1adb16c43 --- /dev/null +++ b/wallets/metamask/src/pages/HomePage/actions/popups/closeRecoveryPhraseReminder.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test' +import { clickLocatorIfCondition } from '../../../../utils/closePopup' +import Selectors from '../../selectors' + +export async function closeRecoveryPhraseReminder(page: Page) { + const closeButtonLocator = page.locator(Selectors.recoveryPhraseReminder.gotItButton) + + // TODO: Extract & make configurable + await clickLocatorIfCondition(closeButtonLocator, () => closeButtonLocator.isVisible(), 1_000) +} diff --git a/wallets/metamask/src/pages/HomePage/actions/popups/index.ts b/wallets/metamask/src/pages/HomePage/actions/popups/index.ts new file mode 100644 index 000000000..2c83dc704 --- /dev/null +++ b/wallets/metamask/src/pages/HomePage/actions/popups/index.ts @@ -0,0 +1,2 @@ +export * from './closePopover' +export * from './closeRecoveryPhraseReminder' diff --git a/wallets/metamask/src/pages/HomePage/actions/settings.ts b/wallets/metamask/src/pages/HomePage/actions/settings.ts index 08f1a00e4..96f561d91 100644 --- a/wallets/metamask/src/pages/HomePage/actions/settings.ts +++ b/wallets/metamask/src/pages/HomePage/actions/settings.ts @@ -1,70 +1,28 @@ -import type { Locator, Page } from '@playwright/test' -import { waitFor } from '../../../utils/waitFor' +import type { Page } from '@playwright/test' +import { toggle } from '../../../utils/toggle' import Selectors from '../selectors' import type { SettingsSidebarMenus } from '../selectors/settings' async function openSettings(page: Page) { - await page.locator(Selectors.accountMenu.accountMenuButton).click() - await page.locator(Selectors.accountMenu.settingsButton).click() + await page.locator(Selectors.threeDotsMenu.threeDotsButton).click() + await page.locator(Selectors.threeDotsMenu.settingsButton).click() } async function openSidebarMenu(page: Page, menu: SettingsSidebarMenus) { await page.locator(Selectors.settings.sidebarMenu(menu)).click() } -async function toggle(toggleLocator: Locator) { - // TODO: Extract timeout - const classes = await toggleLocator.getAttribute('class', { timeout: 3_000 }) - - if (!classes) { - throw new Error('[ToggleShowTestNetworks] Toggle class returned null') - } - - const isOn = classes.includes('toggle-button--on') - - await toggleLocator.click() - - const waitForAction = async () => { - const classes = await toggleLocator.getAttribute('class') - - if (!classes) { - throw new Error('[ToggleShowTestNetworks] Toggle class returned null inside waitFor') - } - - if (isOn) { - return classes.includes('toggle-button--off') - } - - return classes.includes('toggle-button--on') - } - - // TODO: Extract timeout - await waitFor(waitForAction, 3_000, true) -} - -async function toggleShowTestNetworks(page: Page) { - // .nth(0) -> Show conversion on test networks - // .nth(1) -> Show test networks - const toggleLocator = page.locator(Selectors.settings.advanced.showTestNetworksToggle).nth(1) - await toggle(toggleLocator) -} - -async function toggleImprovedTokenAllowanceExperience(page: Page) { - const toggleLocator = page.locator(Selectors.settings.experimental.toggleImprovedTokenAllowanceExperience) +async function toggleDismissSecretRecoveryPhraseReminder(page: Page) { + const toggleLocator = page.locator(Selectors.settings.advanced.dismissSecretRecoveryPhraseReminderToggle) await toggle(toggleLocator) } const advanced = { - toggleShowTestNetworks -} - -const experimental = { - toggleImprovedTokenAllowanceExperience + toggleDismissSecretRecoveryPhraseReminder } export const settings = { openSettings, openSidebarMenu, - advanced, - experimental + advanced } diff --git a/wallets/metamask/src/pages/HomePage/actions/switchAccount.ts b/wallets/metamask/src/pages/HomePage/actions/switchAccount.ts index ca7c4a516..fab808ed5 100644 --- a/wallets/metamask/src/pages/HomePage/actions/switchAccount.ts +++ b/wallets/metamask/src/pages/HomePage/actions/switchAccount.ts @@ -1,12 +1,13 @@ import type { Page } from '@playwright/test' +import { allTextContents } from '../../../utils/allTextContents' import Selectors from '../selectors' export async function switchAccount(page: Page, accountName: string) { - await page.locator(Selectors.accountMenu.accountMenuButton).click() + await page.locator(Selectors.accountMenu.accountButton).click() - const accountNamesLocators = await page.locator(Selectors.accountMenu.accountNamesSelector).all() + const accountNamesLocators = await page.locator(Selectors.accountMenu.accountNames).all() - const accountNames = await page.locator(Selectors.accountMenu.accountNamesSelector).allTextContents() + const accountNames = await allTextContents(accountNamesLocators) const seekedAccountNames = accountNames.filter((name) => name.toLocaleLowerCase() === accountName.toLocaleLowerCase()) diff --git a/wallets/metamask/src/pages/HomePage/actions/switchNetwork.ts b/wallets/metamask/src/pages/HomePage/actions/switchNetwork.ts index e4ef4b29e..35272bcbc 100644 --- a/wallets/metamask/src/pages/HomePage/actions/switchNetwork.ts +++ b/wallets/metamask/src/pages/HomePage/actions/switchNetwork.ts @@ -1,12 +1,14 @@ import type { Page } from '@playwright/test' +import { allTextContents } from '../../../utils/allTextContents' import Selectors from '../selectors' +import { closeRecoveryPhraseReminder } from './popups' export async function switchNetwork(page: Page, networkName: string) { await page.locator(Selectors.networkDropdown.dropdownButton).click() const networkLocators = await page.locator(Selectors.networkDropdown.networks).all() - const networkNames = await page.locator(Selectors.networkDropdown.networks).allTextContents() + const networkNames = await allTextContents(networkLocators) const seekedNetworkNames = networkNames.filter((name) => name.toLocaleLowerCase() === networkName.toLocaleLowerCase()) @@ -19,4 +21,7 @@ export async function switchNetwork(page: Page, networkName: string) { // biome-ignore lint/style/noNonNullAssertion: this non-null assertion is intentional await networkLocators[accountIndex]!.click() // TODO: handle the undefined here better + + // TODO: This is not really needed if we do `metamask.toggleDismissSecretRecoveryPhraseReminder()` by default. Figure this out! + await closeRecoveryPhraseReminder(page) } diff --git a/wallets/metamask/src/pages/HomePage/actions/toggleShowTestNetworks.ts b/wallets/metamask/src/pages/HomePage/actions/toggleShowTestNetworks.ts new file mode 100644 index 000000000..b02ebfebd --- /dev/null +++ b/wallets/metamask/src/pages/HomePage/actions/toggleShowTestNetworks.ts @@ -0,0 +1,11 @@ +import type { Page } from '@playwright/test' +import { toggle } from '../../../utils/toggle' +import Selectors from '../selectors' + +// Toggling this through the network dropdown instead of the settings page is a better approach. +// This is in most cases the faster approach, but it's also more reliable. +export async function toggleShowTestNetworks(page: Page) { + await page.locator(Selectors.networkDropdown.dropdownButton).click() + + await toggle(page.locator(Selectors.networkDropdown.showTestNetworksToggle)) +} diff --git a/wallets/metamask/src/pages/HomePage/page.ts b/wallets/metamask/src/pages/HomePage/page.ts index 648e9bfff..9ff49e30d 100644 --- a/wallets/metamask/src/pages/HomePage/page.ts +++ b/wallets/metamask/src/pages/HomePage/page.ts @@ -1,5 +1,13 @@ import type { Page } from '@playwright/test' -import { addNetwork, importWalletFromPrivateKey, lock, settings, switchAccount, switchNetwork } from './actions' +import { + addNetwork, + importWalletFromPrivateKey, + lock, + settings, + switchAccount, + switchNetwork, + toggleShowTestNetworks +} from './actions' import type { Network } from './actions' import Selectors from './selectors' import type { SettingsSidebarMenus } from './selectors/settings' @@ -39,11 +47,11 @@ export class HomePage { } async toggleShowTestNetworks() { - await settings.advanced.toggleShowTestNetworks(this.page) + await toggleShowTestNetworks(this.page) } - async toggleImprovedTokenAllowanceExperience() { - await settings.experimental.toggleImprovedTokenAllowanceExperience(this.page) + async toggleDismissSecretRecoveryPhraseReminder() { + await settings.advanced.toggleDismissSecretRecoveryPhraseReminder(this.page) } async switchNetwork(networkName: string) { diff --git a/wallets/metamask/src/pages/HomePage/selectors/index.ts b/wallets/metamask/src/pages/HomePage/selectors/index.ts index def680bc6..42697657a 100644 --- a/wallets/metamask/src/pages/HomePage/selectors/index.ts +++ b/wallets/metamask/src/pages/HomePage/selectors/index.ts @@ -1,52 +1,79 @@ import { createDataTestSelector } from '../../../utils/selectors/createDataTestSelector' import settings from './settings' -const container = '.account-menu' +const accountMenuContainer = '.multichain-account-menu-popover' + +const importAccountMenu = { + privateKeyInput: `${accountMenuContainer} input#private-key-box`, + importButton: `${accountMenuContainer} ${createDataTestSelector('import-account-confirm-button')}`, + error: `${accountMenuContainer} p.mm-form-text-field__help-text` +} + +const addAccountMenu = { + addAccountButton: `${accountMenuContainer} ${createDataTestSelector( + 'multichain-account-menu-popover-action-button' + )}`, + addNewAccountButton: `${accountMenuContainer} ${createDataTestSelector( + 'multichain-account-menu-popover-add-account' + )}`, + importAccountButton: `${accountMenuContainer} div.mm-box.mm-box--padding-4:nth-child(2) > div.mm-box:nth-child(2) > button`, + importAccountMenu +} + const accountMenu = { - accountMenuButton: createDataTestSelector('account-menu-icon'), - lockButton: `${container} .account-menu__lock-button`, - importAccountButton: `${container} > .account-menu__item--clickable:nth-of-type(2)`, - settingsButton: `${container} > .account-menu__item--clickable:nth-of-type(5)`, - accountNamesSelector: `${container} ${createDataTestSelector('account-menu__account')} .account-menu__name` + accountButton: createDataTestSelector('account-menu-icon'), + accountNames: `${accountMenuContainer} .multichain-account-menu-popover__list .multichain-account-list-item__account-name__button`, + addAccountMenu +} + +const threeDotsMenu = { + threeDotsButton: createDataTestSelector('account-options-menu-button'), + settingsButton: createDataTestSelector('global-menu-settings'), + lockButton: createDataTestSelector('global-menu-lock') } -const importAccountScreen = { - privateKeyInput: 'input#private-key-box', - importButton: '.new-account-import-form__buttons button.btn-primary', - error: 'span.error' +const popoverContainer = '.popover-container' +const popover = { + closeButton: `${popoverContainer} ${createDataTestSelector('popover-close')}` } const recoveryPhraseReminder = { gotItButton: '.recovery-phrase-reminder button.btn-primary' } +const networkDropdownContainer = '.multichain-network-list-menu-content-wrapper' const networkDropdown = { dropdownButton: createDataTestSelector('network-display'), - networks: `${createDataTestSelector('network-droppo')} .network-dropdown-list li > span`, - addNetworkButton: `${createDataTestSelector('network-droppo')} .network__add-network-button > button` + closeDropdownButton: `${networkDropdownContainer} > section > div:nth-child(1) button`, + networks: `${networkDropdownContainer} .multichain-network-list-menu button`, + showTestNetworksToggle: `${networkDropdownContainer} > section > div:nth-child(3) .toggle-button`, + addNetworkButton: ` ${networkDropdownContainer} div.mm-box.mm-box--padding-4 > button` } const tabContainer = '.tabs__content' const activityTab = { activityTabButton: `${createDataTestSelector('home__activity-tab')}`, transactionsList: `${tabContainer} .transaction-list__transactions`, - pendingTransactionsList: `${tabContainer} .transaction-list__pending-transactions`, - pendingTransactions: `${tabContainer} .transaction-list__pending-transactions .transaction-list-item`, pendingQueuedTransactions: `${tabContainer} .transaction-list__pending-transactions .transaction-list-item .transaction-status-label--queued`, pendingUnapprovedTransactions: `${tabContainer} .transaction-list__pending-transactions .transaction-list-item .transaction-status-label--unapproved`, - pendingApprovedTransactions: `${tabContainer} .transaction-list__pending-transactions .transaction-list-item .transaction-status-label--pending`, - completedTransactionsList: `${tabContainer} .transaction-list__completed-transactions`, - completedTransactions: `${tabContainer} .transaction-list__completed-transactions .transaction-list-item` + pendingApprovedTransactions: `${tabContainer} .transaction-list__pending-transactions .transaction-list-item .transaction-status-label--pending` + + /// ---- Unused Selectors ---- + // pendingTransactionsList: `${tabContainer} .transaction-list__pending-transactions`, + // pendingTransactions: `${tabContainer} .transaction-list__pending-transactions .transaction-list-item`, + // completedTransactionsList: `${tabContainer} .transaction-list__completed-transactions`, + // completedTransactions: `${tabContainer} .transaction-list__completed-transactions .transaction-list-item` } export default { - logo: createDataTestSelector('app-header-logo'), - account: createDataTestSelector('selected-account-click'), - currentNetwork: createDataTestSelector('network-display'), + logo: `button${createDataTestSelector('app-header-logo')}`, + copyAccountAddressButton: createDataTestSelector('address-copy-button-text'), + currentNetwork: `${createDataTestSelector('network-display')} span:nth-of-type(1)`, + threeDotsMenu, settings, activityTab, networkDropdown, accountMenu, - importAccountScreen, - recoveryPhraseReminder + recoveryPhraseReminder, + popover } diff --git a/wallets/metamask/src/pages/HomePage/selectors/settings.ts b/wallets/metamask/src/pages/HomePage/selectors/settings.ts index 75d363f8e..29429aa3b 100644 --- a/wallets/metamask/src/pages/HomePage/selectors/settings.ts +++ b/wallets/metamask/src/pages/HomePage/selectors/settings.ts @@ -2,23 +2,24 @@ import { createDataTestSelector } from '../../../utils/selectors/createDataTestS export enum SettingsSidebarMenus { General = 1, - Advanced = 2, - Contacts = 3, - SecurityAndPrivacy = 4, - Alerts = 5, - Networks = 6, - Experimental = 7, - About = 8 + Advanced = 2 + + /// ---- Unused Selectors ---- + // Contacts = 3, + // SecurityAndPrivacy = 4, + // Alerts = 5, + // Networks = 6, + // Experimental = 7, + // About = 8 } const sidebarMenu = (menu: SettingsSidebarMenus) => `.settings-page__content__tabs .tab-bar__tab.pointer:nth-of-type(${menu})` const advanced = { - showTestNetworksToggle: `${createDataTestSelector('advanced-setting-show-testnet-conversion')} .toggle-button` -} - -const experimental = { - toggleImprovedTokenAllowanceExperience: '.settings-page__content-item .toggle-button' + // locator(showTestNetworksToggle).nth(0) -> Show conversion on test networks + // locator(showTestNetworksToggle).nth(1) -> Show test networks + showTestNetworksToggle: `${createDataTestSelector('advanced-setting-show-testnet-conversion')} .toggle-button`, + dismissSecretRecoveryPhraseReminderToggle: '.settings-page__content-row:nth-of-type(11) .toggle-button' } const newNetworkFormContainer = '.networks-tab__add-network-form' @@ -30,8 +31,10 @@ const newNetworkForm = { chainIdError: `${newNetworkFormContainer} .form-field:nth-child(3) .form-field__error`, symbolInput: `${newNetworkFormContainer} .form-field:nth-child(4) input`, blockExplorerUrlInput: `${newNetworkFormContainer} .form-field:nth-child(5) input`, - cancelButton: `${newNetworkFormContainer} .networks-tab__add-network-form-footer button.btn-secondary`, saveButton: `${newNetworkFormContainer} .networks-tab__add-network-form-footer button.btn-primary` + + /// ---- Unused Selectors ---- + // cancelButton: `${newNetworkFormContainer} .networks-tab__add-network-form-footer button.btn-secondary`, } const networks = { @@ -43,6 +46,5 @@ export default { SettingsSidebarMenus, sidebarMenu, advanced, - experimental, networks } diff --git a/wallets/metamask/src/pages/NotificationPage/actions/approvePermission.ts b/wallets/metamask/src/pages/NotificationPage/actions/approvePermission.ts index 8a53e0283..a1fcd279d 100644 --- a/wallets/metamask/src/pages/NotificationPage/actions/approvePermission.ts +++ b/wallets/metamask/src/pages/NotificationPage/actions/approvePermission.ts @@ -1,48 +1,31 @@ import type { Page } from '@playwright/test' import Selectors from '../selectors' -const editTokenPermission = async (notificationPage: Page, customSpendLimit: number) => { - await notificationPage.locator(Selectors.PermissionPage.approve.editPermission.editPermissionButton).click() - - await notificationPage.locator(Selectors.PermissionPage.approve.editPermission.customSpendLimitButton).click() - - await notificationPage - .locator(Selectors.PermissionPage.approve.editPermission.customSpendLimitInput) - .fill(customSpendLimit.toString()) - - await notificationPage.locator(Selectors.PermissionPage.approve.editPermission.saveButton).click() -} - -const editTokenPermissionWithImprovedTokenAllowanceExperience = async ( - notificationPage: Page, - customSpendLimit: 'default' | 'max' | number -) => { - if (customSpendLimit === 'default') { - await notificationPage.locator(Selectors.PermissionPage.improvedApprove.useDefaultButton).click() - return - } - +const editTokenPermission = async (notificationPage: Page, customSpendLimit: 'max' | number) => { if (customSpendLimit === 'max') { - await notificationPage.locator(Selectors.PermissionPage.improvedApprove.maxButton).click() + await notificationPage.locator(Selectors.PermissionPage.approve.maxButton).click() return } await notificationPage - .locator(Selectors.PermissionPage.improvedApprove.customSpendingCapInput) + .locator(Selectors.PermissionPage.approve.customSpendingCapInput) .fill(customSpendLimit.toString()) } const approveTokenPermission = async (notificationPage: Page) => { - await notificationPage.locator(Selectors.PermissionPage.approve.confirmButton).click() + // Click the "Next" button. + await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() + + // Click the "Confirm" button. + await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() } const rejectTokenPermission = async (notificationPage: Page) => { - await notificationPage.locator(Selectors.PermissionPage.approve.rejectButton).click() + await notificationPage.locator(Selectors.ActionFooter.rejectActionButton).click() } export const approvePermission = { - editSpendLimit: editTokenPermission, - editSpendLimitWithImprovedTokenAllowanceExperience: editTokenPermissionWithImprovedTokenAllowanceExperience, + editTokenPermission, approve: approveTokenPermission, reject: rejectTokenPermission } diff --git a/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts b/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts index 16f89413a..9b8bf6d48 100644 --- a/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts +++ b/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts @@ -1,12 +1,10 @@ -import type { BrowserContext } from '@playwright/test' -import { getNotificationPageAndWaitForLoad } from '../../../utils/getNotificationPageAndWaitForLoad' - -export async function connectToDapp(context: BrowserContext, extensionId: string) { - const notificationPage = await getNotificationPageAndWaitForLoad(context, extensionId) +import type { Page } from '@playwright/test' +import Selectors from '../selectors' +export async function connectToDapp(notificationPage: Page) { // Click `Next`. - await notificationPage.getByRole('button').nth(1).click() + await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() // Click `Connect`. - await notificationPage.getByRole('button').nth(1).click() + await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() } diff --git a/wallets/metamask/src/pages/NotificationPage/actions/signSimpleMessage.ts b/wallets/metamask/src/pages/NotificationPage/actions/signSimpleMessage.ts index 6a3bc4cb9..8e973acdf 100644 --- a/wallets/metamask/src/pages/NotificationPage/actions/signSimpleMessage.ts +++ b/wallets/metamask/src/pages/NotificationPage/actions/signSimpleMessage.ts @@ -2,11 +2,11 @@ import type { Page } from '@playwright/test' import Selectors from '../selectors' const signMessage = async (notificationPage: Page) => { - await notificationPage.locator(Selectors.SignaturePage.simpleMessage.signButton).click() + await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() } const rejectMessage = async (notificationPage: Page) => { - await notificationPage.locator(Selectors.SignaturePage.simpleMessage.rejectButton).click() + await notificationPage.locator(Selectors.ActionFooter.rejectActionButton).click() } // Used for: diff --git a/wallets/metamask/src/pages/NotificationPage/actions/signStructuredMessage.ts b/wallets/metamask/src/pages/NotificationPage/actions/signStructuredMessage.ts index b0aef2d6c..494a54714 100644 --- a/wallets/metamask/src/pages/NotificationPage/actions/signStructuredMessage.ts +++ b/wallets/metamask/src/pages/NotificationPage/actions/signStructuredMessage.ts @@ -3,17 +3,17 @@ import Selectors from '../selectors' const signMessage = async (notificationPage: Page) => { const scrollDownButton = notificationPage.locator(Selectors.SignaturePage.structuredMessage.scrollDownButton) - const signButton = notificationPage.locator(Selectors.SignaturePage.structuredMessage.signButton) + const signButton = notificationPage.locator(Selectors.ActionFooter.confirmActionButton) while (await signButton.isDisabled()) { await scrollDownButton.click() } - await notificationPage.locator(Selectors.SignaturePage.structuredMessage.signButton).click() + await signButton.click() } const rejectMessage = async (notificationPage: Page) => { - await notificationPage.locator(Selectors.SignaturePage.structuredMessage.rejectButton).click() + await notificationPage.locator(Selectors.ActionFooter.rejectActionButton).click() } // Used for: diff --git a/wallets/metamask/src/pages/NotificationPage/actions/transaction.ts b/wallets/metamask/src/pages/NotificationPage/actions/transaction.ts index 230f029f6..e12e8f4a0 100644 --- a/wallets/metamask/src/pages/NotificationPage/actions/transaction.ts +++ b/wallets/metamask/src/pages/NotificationPage/actions/transaction.ts @@ -4,7 +4,7 @@ import HomePageSelectors from '../../HomePage/selectors' import Selectors from '../selectors' const confirmTransaction = async (notificationPage: Page) => { - await notificationPage.locator(Selectors.TransactionPage.confirmButton).click() + await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() } const confirmTransactionAndWaitForMining = async (walletPage: Page, notificationPage: Page) => { @@ -42,7 +42,7 @@ const confirmTransactionAndWaitForMining = async (walletPage: Page, notification } const rejectTransaction = async (notificationPage: Page) => { - await notificationPage.locator(Selectors.TransactionPage.rejectButton).click() + await notificationPage.locator(Selectors.ActionFooter.rejectActionButton).click() } export const transaction = { diff --git a/wallets/metamask/src/pages/NotificationPage/page.ts b/wallets/metamask/src/pages/NotificationPage/page.ts index 583349cbf..cd1465698 100644 --- a/wallets/metamask/src/pages/NotificationPage/page.ts +++ b/wallets/metamask/src/pages/NotificationPage/page.ts @@ -1,5 +1,4 @@ import type { Page } from '@playwright/test' -import { z } from 'zod' import { getNotificationPageAndWaitForLoad } from '../../utils/getNotificationPageAndWaitForLoad' import { waitFor } from '../../utils/waitFor' import { @@ -23,7 +22,9 @@ export class NotificationPage { } async connectToDapp(extensionId: string) { - await connectToDapp(this.page.context(), extensionId) + const notificationPage = await getNotificationPageAndWaitForLoad(this.page.context(), extensionId) + + await connectToDapp(notificationPage) } // TODO: Revisit this logic in the future to see if we can increase the performance by utilizing `Promise.race`. @@ -106,28 +107,11 @@ export class NotificationPage { await transaction.confirmAndWaitForMining(this.page, notificationPage) } - // TODO: Revisit this in the future and make the improved token allowance experience enabled by default. - async approvePermission(extensionId: string, spendLimit: 'default' | 'max' | number = 'default') { + async approvePermission(extensionId: string, spendLimit?: 'max' | number) { const notificationPage = await getNotificationPageAndWaitForLoad(this.page.context(), extensionId) - const isImprovedTokenAllowanceEnabled = await waitFor( - () => notificationPage.locator(Selectors.PermissionPage.improvedApprove.customSpendingCapInput).isVisible(), - 1_500, - false - ) - - if (isImprovedTokenAllowanceEnabled) { - await approvePermission.editSpendLimitWithImprovedTokenAllowanceExperience(notificationPage, spendLimit) - } else { - if (spendLimit !== 'default') { - const parsedCustomSpentLimit = z - .number({ - invalid_type_error: - 'Custom spend limit must be a number if the improved token allowance experience is disabled' - }) - .parse(spendLimit) - await approvePermission.editSpendLimit(notificationPage, parsedCustomSpentLimit) - } + if (spendLimit !== undefined) { + await approvePermission.editTokenPermission(notificationPage, spendLimit) } await approvePermission.approve(notificationPage) diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/actionFooter.ts b/wallets/metamask/src/pages/NotificationPage/selectors/actionFooter.ts new file mode 100644 index 000000000..fb1504b24 --- /dev/null +++ b/wallets/metamask/src/pages/NotificationPage/selectors/actionFooter.ts @@ -0,0 +1,6 @@ +import { createDataTestSelector } from '../../../utils/selectors/createDataTestSelector' + +export default { + confirmActionButton: `.page-container__footer ${createDataTestSelector('page-container-footer-next')}`, + rejectActionButton: `.page-container__footer ${createDataTestSelector('page-container-footer-cancel')}` +} diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/index.ts b/wallets/metamask/src/pages/NotificationPage/selectors/index.ts index 42a9037d4..ad5ddbd71 100644 --- a/wallets/metamask/src/pages/NotificationPage/selectors/index.ts +++ b/wallets/metamask/src/pages/NotificationPage/selectors/index.ts @@ -1,11 +1,11 @@ +import ActionFooter from './actionFooter' import NetworkPage from './networkPage' import PermissionPage from './permissionPage' import SignaturePage from './signaturePage' -import TransactionPage from './transactionPage' export default { + ActionFooter, SignaturePage, NetworkPage, - TransactionPage, PermissionPage } diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/permissionPage.ts b/wallets/metamask/src/pages/NotificationPage/selectors/permissionPage.ts index b445bc67c..f824e56cc 100644 --- a/wallets/metamask/src/pages/NotificationPage/selectors/permissionPage.ts +++ b/wallets/metamask/src/pages/NotificationPage/selectors/permissionPage.ts @@ -1,28 +1,10 @@ import { createDataTestSelector } from '../../../utils/selectors/createDataTestSelector' const approve = { - editPermission: { - editPermissionButton: - '.confirm-approve-content__edit-submission-button-container .confirm-approve-content__medium-link-text.cursor-pointer', - customSpendLimitButton: - '.edit-approval-permission-modal-content .edit-approval-permission__edit-section__option:last-of-type .edit-approval-permission__edit-section__radio-button', - customSpendLimitInput: - '.edit-approval-permission-modal-content .edit-approval-permission__edit-section__option-input input', - saveButton: '.edit-approval-permission-modal-container .modal-container__footer > button.btn-primary' - }, - confirmButton: `.page-container__footer ${createDataTestSelector('page-container-footer-next')}`, - rejectButton: `.page-container__footer ${createDataTestSelector('page-container-footer-cancel')}` -} - -const improvedApprove = { - useDefaultButton: '.custom-spending-cap .form-field__heading button', - maxButton: '.custom-spending-cap .custom-spending-cap__max button', - customSpendingCapInput: `.custom-spending-cap ${createDataTestSelector('custom-spending-cap-input')}`, - confirmButton: `.page-container__footer ${createDataTestSelector('page-container-footer-next')}`, - rejectButton: `.page-container__footer ${createDataTestSelector('page-container-footer-cancel')}` + maxButton: createDataTestSelector('custom-spending-cap-max-button'), + customSpendingCapInput: createDataTestSelector('custom-spending-cap-input') } export default { - approve, - improvedApprove + approve } diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/transactionPage.ts b/wallets/metamask/src/pages/NotificationPage/selectors/transactionPage.ts deleted file mode 100644 index c039dcb79..000000000 --- a/wallets/metamask/src/pages/NotificationPage/selectors/transactionPage.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createDataTestSelector } from '../../../utils/selectors/createDataTestSelector' - -export default { - confirmButton: `.page-container__footer ${createDataTestSelector('page-container-footer-next')}`, - rejectButton: `.page-container__footer ${createDataTestSelector('page-container-footer-cancel')}` -} diff --git a/wallets/metamask/src/pages/OnboardingPage/actions/importWallet.ts b/wallets/metamask/src/pages/OnboardingPage/actions/importWallet.ts index ada53835d..0894dd86d 100644 --- a/wallets/metamask/src/pages/OnboardingPage/actions/importWallet.ts +++ b/wallets/metamask/src/pages/OnboardingPage/actions/importWallet.ts @@ -1,8 +1,10 @@ import type { Page } from '@playwright/test' +import { closePopover } from '../../HomePage/actions' import Selectors from '../selectors' import { confirmSecretRecoveryPhrase, createPassword } from './helpers' export async function importWallet(page: Page, seedPhrase: string, password: string) { + await page.locator(Selectors.GetStartedPageSelectors.termsOfServiceCheckbox).click() await page.locator(Selectors.GetStartedPageSelectors.importWallet).click() await page.locator(Selectors.AnalyticsPageSelectors.optOut).click() @@ -15,4 +17,6 @@ export async function importWallet(page: Page, seedPhrase: string, password: str await page.locator(Selectors.PinExtensionPageSelectors.nextButton).click() await page.locator(Selectors.PinExtensionPageSelectors.confirmButton).click() + + await closePopover(page) } diff --git a/wallets/metamask/src/pages/OnboardingPage/selectors/getStartedPage.ts b/wallets/metamask/src/pages/OnboardingPage/selectors/getStartedPage.ts index 37e2b735f..200d1adb2 100644 --- a/wallets/metamask/src/pages/OnboardingPage/selectors/getStartedPage.ts +++ b/wallets/metamask/src/pages/OnboardingPage/selectors/getStartedPage.ts @@ -1,6 +1,7 @@ import { createDataTestSelector } from '../../../utils/selectors/createDataTestSelector' export default { + termsOfServiceCheckbox: createDataTestSelector('onboarding-terms-checkbox'), createNewWallet: createDataTestSelector('onboarding-create-wallet'), importWallet: createDataTestSelector('onboarding-import-wallet') } diff --git a/wallets/metamask/src/pages/OnboardingPage/selectors/secretRecoveryPhrasePage.ts b/wallets/metamask/src/pages/OnboardingPage/selectors/secretRecoveryPhrasePage.ts index 3f97cd933..1bce0f81a 100644 --- a/wallets/metamask/src/pages/OnboardingPage/selectors/secretRecoveryPhrasePage.ts +++ b/wallets/metamask/src/pages/OnboardingPage/selectors/secretRecoveryPhrasePage.ts @@ -5,7 +5,7 @@ const recoveryStep = { selectNumberOfWordsOption: (option: number | string) => `${option}`, secretRecoveryPhraseWord: (index: number) => createDataTestSelector(`import-srp__srp-word-${index}`), confirmSecretRecoveryPhraseButton: createDataTestSelector('import-srp-confirm'), - error: '.actionable-message.actionable-message--danger.import-srp__srp-error > .actionable-message__message' + error: '.mm-banner-alert.import-srp__srp-error div' } const passwordStep = { @@ -13,7 +13,7 @@ const passwordStep = { confirmPasswordInput: createDataTestSelector('create-password-confirm'), acceptTermsCheckbox: createDataTestSelector('create-password-terms'), importWalletButton: createDataTestSelector('create-password-import'), - error: `${createDataTestSelector('create-password-confirm')} + h6` + error: `${createDataTestSelector('create-password-new')} + h6 > span > span` } export default { diff --git a/wallets/metamask/src/prepareExtension.ts b/wallets/metamask/src/prepareExtension.ts index 98f2ec8fd..93533c03b 100644 --- a/wallets/metamask/src/prepareExtension.ts +++ b/wallets/metamask/src/prepareExtension.ts @@ -1,6 +1,6 @@ import { downloadFile, ensureCacheDirExists, unzipArchive } from 'core' -export const DEFAULT_METAMASK_VERSION = '10.25.0' +export const DEFAULT_METAMASK_VERSION = '11.5.1' export const EXTENSION_DOWNLOAD_URL = `https://github.com/MetaMask/metamask-extension/releases/download/v${DEFAULT_METAMASK_VERSION}/metamask-chrome-${DEFAULT_METAMASK_VERSION}.zip` export async function prepareExtension() { diff --git a/wallets/metamask/src/utils/allTextContents.ts b/wallets/metamask/src/utils/allTextContents.ts new file mode 100644 index 000000000..b5712915c --- /dev/null +++ b/wallets/metamask/src/utils/allTextContents.ts @@ -0,0 +1,10 @@ +import type { Locator } from '@playwright/test' +import { z } from 'zod' + +// Custom implementation of `locator.allTextContents()` that is not utilizing `.map` which is not accessible under MetaMask's scuttling mode. +export async function allTextContents(locators: Locator[]) { + const names = await Promise.all(locators.map((locator) => locator.textContent())) + + // We're making sure that the return type is `string[]` same as `locator.allTextContents()`. + return names.map((name) => z.string().parse(name)) +} diff --git a/wallets/metamask/src/utils/closePopup.ts b/wallets/metamask/src/utils/closePopup.ts new file mode 100644 index 000000000..ce919a455 --- /dev/null +++ b/wallets/metamask/src/utils/closePopup.ts @@ -0,0 +1,10 @@ +import type { Locator } from '@playwright/test' +import { waitFor } from './waitFor' + +// TODO: Extract & make configurable +export async function clickLocatorIfCondition(locator: Locator, condition: () => Promise, timeout = 3_000) { + const shouldClick = await waitFor(condition, timeout, false) + if (shouldClick) { + await locator.click() + } +} diff --git a/wallets/metamask/src/utils/toggle.ts b/wallets/metamask/src/utils/toggle.ts new file mode 100644 index 000000000..cfdcc8354 --- /dev/null +++ b/wallets/metamask/src/utils/toggle.ts @@ -0,0 +1,32 @@ +import type { Locator } from '@playwright/test' +import { waitFor } from './waitFor' + +export async function toggle(toggleLocator: Locator) { + // TODO: Extract timeout + const classes = await toggleLocator.getAttribute('class', { timeout: 3_000 }) + + if (!classes) { + throw new Error('[ToggleShowTestNetworks] Toggle class returned null') + } + + const isOn = classes.includes('toggle-button--on') + + await toggleLocator.click() + + const waitForAction = async () => { + const classes = await toggleLocator.getAttribute('class') + + if (!classes) { + throw new Error('[ToggleShowTestNetworks] Toggle class returned null inside waitFor') + } + + if (isOn) { + return classes.includes('toggle-button--off') + } + + return classes.includes('toggle-button--on') + } + + // TODO: Extract timeout + await waitFor(waitForAction, 3_000, true) +} diff --git a/wallets/metamask/src/utils/waitForSpinnerToVanish.ts b/wallets/metamask/src/utils/waitForSpinnerToVanish.ts index b821dd178..077ee5c68 100644 --- a/wallets/metamask/src/utils/waitForSpinnerToVanish.ts +++ b/wallets/metamask/src/utils/waitForSpinnerToVanish.ts @@ -2,7 +2,7 @@ import type { Page } from '@playwright/test' import { LoadingSelectors } from '../selectors' // TODO: Not sure if hard coding the timeout is a good idea but must be enough for now. -const DEFAULT_TIMEOUT = 3_000 +const DEFAULT_TIMEOUT = 10_000 export async function waitForSpinnerToVanish(page: Page) { await page.locator(LoadingSelectors.spinner).waitFor({ diff --git a/wallets/metamask/test/e2e/metamask/approvePermission.spec.ts b/wallets/metamask/test/e2e/metamask/approvePermission.spec.ts index f4b176b33..c3d0ed790 100644 --- a/wallets/metamask/test/e2e/metamask/approvePermission.spec.ts +++ b/wallets/metamask/test/e2e/metamask/approvePermission.spec.ts @@ -2,7 +2,6 @@ import { testWithMetaMask } from '../testWithMetaMask' const test = testWithMetaMask.extend<{ switchChainAndDeployToken: () => Promise - enableImprovedTokenAllowanceExperience: () => Promise }>({ switchChainAndDeployToken: async ({ page, metamask }, use) => { await use(async () => { @@ -16,98 +15,38 @@ const test = testWithMetaMask.extend<{ await metamask.confirmTransaction() }) - }, - enableImprovedTokenAllowanceExperience: async ({ metamask }, use) => { - await use(async () => { - await metamask.openSettings() - - const SidebarMenus = metamask.homePage.selectors.settings.SettingsSidebarMenus - await metamask.openSidebarMenu(SidebarMenus.Experimental) - - await metamask.toggleImprovedTokenAllowanceExperience() - - await metamask.goBackToHomePage() - }) } }) -const { expect, describe } = test +const { expect } = test // These tests rely on the same account, which means they must be run in serial. test.describe.configure({ mode: 'serial' }) -describe('with improved token allowance experience disabled', () => { - test('should approve tokens with the default limit', async ({ page, metamask, switchChainAndDeployToken }) => { - await switchChainAndDeployToken() - - await page.locator('#approveTokens').click() - - await metamask.approvePermission() - }) - - test('should approve tokens with the custom limit', async ({ page, metamask, switchChainAndDeployToken }) => { - await switchChainAndDeployToken() +test('should approve tokens with the default limit by default', async ({ + page, + metamask, + switchChainAndDeployToken +}) => { + await switchChainAndDeployToken() - await page.locator('#approveTokens').click() + await page.locator('#approveTokens').click() - await metamask.approvePermission(420) - }) + await metamask.approvePermission() }) -describe('with improved token allowance experience enabled', () => { - test('should approve tokens with the default limit', async ({ - page, - metamask, - switchChainAndDeployToken, - enableImprovedTokenAllowanceExperience - }) => { - await enableImprovedTokenAllowanceExperience() - await switchChainAndDeployToken() +test('should approve tokens with the max limit', async ({ page, metamask, switchChainAndDeployToken }) => { + await switchChainAndDeployToken() - await page.locator('#approveTokens').click() + await page.locator('#approveTokens').click() - await metamask.approvePermission('default') - }) - - test('should approve tokens with the default limit by default', async ({ - page, - metamask, - switchChainAndDeployToken, - enableImprovedTokenAllowanceExperience - }) => { - await enableImprovedTokenAllowanceExperience() - await switchChainAndDeployToken() - - await page.locator('#approveTokens').click() - - await metamask.approvePermission() - }) - - test('should approve tokens with the max limit', async ({ - page, - metamask, - switchChainAndDeployToken, - enableImprovedTokenAllowanceExperience - }) => { - await enableImprovedTokenAllowanceExperience() - await switchChainAndDeployToken() - - await page.locator('#approveTokens').click() - - await metamask.approvePermission('max') - }) + await metamask.approvePermission('max') +}) - test('should approve tokens with the custom limit', async ({ - page, - metamask, - switchChainAndDeployToken, - enableImprovedTokenAllowanceExperience - }) => { - await enableImprovedTokenAllowanceExperience() - await switchChainAndDeployToken() +test('should approve tokens with the custom limit', async ({ page, metamask, switchChainAndDeployToken }) => { + await switchChainAndDeployToken() - await page.locator('#approveTokens').click() + await page.locator('#approveTokens').click() - await metamask.approvePermission(420) - }) + await metamask.approvePermission(420) }) diff --git a/wallets/metamask/test/e2e/metamask/confirmSignature.spec.ts b/wallets/metamask/test/e2e/metamask/confirmSignature.spec.ts index 861821cbc..7cf9beccd 100644 --- a/wallets/metamask/test/e2e/metamask/confirmSignature.spec.ts +++ b/wallets/metamask/test/e2e/metamask/confirmSignature.spec.ts @@ -57,7 +57,7 @@ test('should confirm `eth_signTypedData_v4`', async ({ page, metamask }) => { await metamask.confirmSignature() await expect(page.locator('#signTypedDataV4Result')).toHaveText( - '0x789d9365fe0fbf1485b8069cbb000b78abd56b92608f9bc11a0d78e8810cd0434a60e93790c52348e5ac8770a8c5b0bb89411c2fbc61cbb4f56d67d60a3374961c' + '0x1cf422c4a319c19ecb89c960e7c296810278fa2bef256c7e9419b285c8216c547b3371fa1ec3987ce08561d3ed779845393d8d3e4311376d0bc0846f37d1b2821c' ) await page.locator('#signTypedDataV4Verify').click() diff --git a/wallets/metamask/test/e2e/metamask/confirmTransaction.spec.ts b/wallets/metamask/test/e2e/metamask/confirmTransaction.spec.ts index 4465a7e8c..2ffc4caa8 100644 --- a/wallets/metamask/test/e2e/metamask/confirmTransaction.spec.ts +++ b/wallets/metamask/test/e2e/metamask/confirmTransaction.spec.ts @@ -4,6 +4,9 @@ const test = testWithMetaMask const { expect } = test +// These tests rely on the same account, which means they must be run in serial. +test.describe.configure({ mode: 'serial' }) + test('should confirm contract deployment', async ({ page, metamask }) => { await page.locator('#addEthereumChain').click() diff --git a/wallets/metamask/test/e2e/metamask/confirmTransactionAndWaitForMining.spec.ts b/wallets/metamask/test/e2e/metamask/confirmTransactionAndWaitForMining.spec.ts index bc7de0d1d..09aeccda9 100644 --- a/wallets/metamask/test/e2e/metamask/confirmTransactionAndWaitForMining.spec.ts +++ b/wallets/metamask/test/e2e/metamask/confirmTransactionAndWaitForMining.spec.ts @@ -46,6 +46,7 @@ test.skip('should confirm EIP 1559 transaction and wait for mining', async ({ pa }) test.skip('should work correctly when calling sequentially', async ({ page, metamask }) => { + // This test takes a looooooooong time, so we need to increase the test timeout. test.setTimeout(120_000) await page.locator('#addEthereumChain').click() diff --git a/wallets/metamask/test/e2e/metamask/goBackToHomePage.spec.ts b/wallets/metamask/test/e2e/metamask/goBackToHomePage.spec.ts index 393fc398d..b4e3bfa7b 100644 --- a/wallets/metamask/test/e2e/metamask/goBackToHomePage.spec.ts +++ b/wallets/metamask/test/e2e/metamask/goBackToHomePage.spec.ts @@ -12,9 +12,9 @@ test('should go back to the home page', async ({ context, metamaskPage }) => { await metamask.openSettings() - await expect(metamaskPage.locator(metamask.homePage.selectors.account)).not.toBeVisible() + await expect(metamaskPage.locator(metamask.homePage.selectors.copyAccountAddressButton)).not.toBeVisible() await metamask.goBackToHomePage() - await expect(metamaskPage.locator(metamask.homePage.selectors.account)).toBeVisible() + await expect(metamaskPage.locator(metamask.homePage.selectors.copyAccountAddressButton)).toBeVisible() }) diff --git a/wallets/metamask/test/e2e/metamask/importWallet.spec.ts b/wallets/metamask/test/e2e/metamask/importWallet.spec.ts index bbf1c439b..436e21d68 100644 --- a/wallets/metamask/test/e2e/metamask/importWallet.spec.ts +++ b/wallets/metamask/test/e2e/metamask/importWallet.spec.ts @@ -1,22 +1,14 @@ -import { type BrowserContext, type Page, chromium, test as base } from '@playwright/test' +import { type Page, chromium, test as base } from '@playwright/test' import { MetaMask } from '../../../src' import { prepareExtension } from '../../../src/prepareExtension' const SEED_PHRASE = 'test test test test test test test test test test test junk' const PASSWORD = 'Tester@1234' -let sharedContext: BrowserContext | undefined - const test = base.extend<{ metamaskPage: Page }>({ context: async ({ context: _ }, use) => { - if (sharedContext) { - await use(sharedContext) - - return - } - const metamaskPath = await prepareExtension() // biome-ignore format: the array should not be formatted @@ -40,8 +32,9 @@ const test = base.extend<{ throw new Error('[FIXTURE] MetaMask extension did not load in time') } - sharedContext = context await use(context) + + await context.close() }, metamaskPage: async ({ context }, use) => { const metamaskOnboardingPage = context.pages()[1] as Page @@ -57,5 +50,27 @@ test('should go through the onboarding flow and import wallet from seed phrase', await metamask.importWallet(SEED_PHRASE) await expect(metamaskPage.getByText('Account 1')).toBeVisible() - await expect(metamaskPage.getByText('0xf39...2266')).toBeVisible() + await expect(metamaskPage.getByText('0xf39Fd...92266')).toBeVisible() +}) + +test('should throw an error due to incorrect seed phrase', async ({ context, metamaskPage }) => { + const metamask = new MetaMask(context, metamaskPage, PASSWORD) + + // Last word is incorrect. + const incorrectSeedPhrase = 'test test test test test test test test test test test jun' + + await expect(metamask.importWallet(incorrectSeedPhrase)).rejects.toThrowError( + '[ConfirmSecretRecoveryPhrase] Invalid seed phrase. Error from MetaMask: Invalid Secret Recovery Phrase' + ) +}) + +test('should throw an error due to incorrect password', async ({ context, metamaskPage }) => { + // Minimum length is 8 characters. + const incorrectPassword = 'test' + + const metamask = new MetaMask(context, metamaskPage, incorrectPassword) + + await expect(metamask.importWallet(SEED_PHRASE)).rejects.toThrowError( + '[CreatePassword] Invalid password. Error from MetaMask: Password not long enough' + ) }) diff --git a/wallets/metamask/test/e2e/metamask/importWalletFromPrivateKey.spec.ts b/wallets/metamask/test/e2e/metamask/importWalletFromPrivateKey.spec.ts index 40a4928d0..2a636dd3b 100644 --- a/wallets/metamask/test/e2e/metamask/importWalletFromPrivateKey.spec.ts +++ b/wallets/metamask/test/e2e/metamask/importWalletFromPrivateKey.spec.ts @@ -7,19 +7,12 @@ const test = testWithSynpress(basicSetup, unlockForFixture) const { expect } = test -test.use({ - permissions: ['clipboard-read'] -}) - test('should import a new wallet from private key', async ({ context, metamaskPage }) => { const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword) await metamask.importWalletFromPrivateKey('ea084c575a01e2bbefcca3db101eaeab1d8af15554640a510c73692db24d0a6a') - await metamaskPage.locator(metamask.homePage.selectors.account).click() - - const accountAddressInClipboard = await metamaskPage.evaluate('navigator.clipboard.readText()') - expect(accountAddressInClipboard).toContain('0xa2ce797cA71d0EaE1be5a7EffD27Fd6C38126801') + await expect(metamaskPage.locator(metamask.homePage.selectors.copyAccountAddressButton)).toHaveText('0xa2ce7...26801') }) test('should throw an error if trying to import private key for the 2nd time', async ({ context, metamaskPage }) => { @@ -29,12 +22,10 @@ test('should throw an error if trying to import private key for the 2nd time', a await metamask.importWalletFromPrivateKey(privateKey) - await metamaskPage.locator(metamask.homePage.selectors.account).click() - const importWalletPromise = metamask.importWalletFromPrivateKey(privateKey) await expect(importWalletPromise).rejects.toThrowError( - '[ImportWalletFromPrivateKey] Importing failed due to error: The account you are trying to import is a duplicate' + '[ImportWalletFromPrivateKey] Importing failed due to error: KeyringController - The account you are trying to import is a duplicate' ) }) diff --git a/wallets/metamask/test/e2e/metamask/rejectSignature.spec.ts b/wallets/metamask/test/e2e/metamask/rejectSignature.spec.ts index 9011d6796..e4bb800f6 100644 --- a/wallets/metamask/test/e2e/metamask/rejectSignature.spec.ts +++ b/wallets/metamask/test/e2e/metamask/rejectSignature.spec.ts @@ -10,7 +10,7 @@ test('should reject `personal_sign`', async ({ page, metamask }) => { await metamask.rejectSignature() await expect(page.locator('#personalSign')).toHaveText( - 'Error: MetaMask Message Signature: User denied message signature.' + 'Error: MetaMask Personal Message Signature: User denied message signature.' ) await expect(page.locator('#personalSignResult')).toHaveText('') }) @@ -21,7 +21,7 @@ test('should reject `eth_signTypedData`', async ({ page, metamask }) => { await metamask.rejectSignature() await expect(page.locator('#signTypedDataResult')).toHaveText( - 'Error: MetaMask Message Signature: User denied message signature.' + 'Error: MetaMask Typed Message Signature: User denied message signature.' ) }) @@ -31,7 +31,7 @@ test('should reject `eth_signTypedData_v3`', async ({ page, metamask }) => { await metamask.rejectSignature() await expect(page.locator('#signTypedDataV3Result')).toHaveText( - 'Error: MetaMask Message Signature: User denied message signature.' + 'Error: MetaMask Typed Message Signature: User denied message signature.' ) }) @@ -41,6 +41,6 @@ test('should reject `eth_signTypedData_v4`', async ({ page, metamask }) => { await metamask.rejectSignature() await expect(page.locator('#signTypedDataV4Result')).toHaveText( - 'Error: MetaMask Message Signature: User denied message signature.' + 'Error: MetaMask Typed Message Signature: User denied message signature.' ) }) diff --git a/wallets/metamask/test/e2e/metamask/switchAccount.spec.ts b/wallets/metamask/test/e2e/metamask/switchAccount.spec.ts index e555d75e6..a56b2ca0a 100644 --- a/wallets/metamask/test/e2e/metamask/switchAccount.spec.ts +++ b/wallets/metamask/test/e2e/metamask/switchAccount.spec.ts @@ -7,10 +7,6 @@ const test = testWithSynpress(basicSetup, unlockForFixture) const { expect } = test -test.use({ - permissions: ['clipboard-read'] -}) - test('should switch account', async ({ context, metamaskPage }) => { const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword) @@ -19,10 +15,8 @@ test('should switch account', async ({ context, metamaskPage }) => { await metamask.switchAccount('Account 1') - await metamaskPage.locator(metamask.homePage.selectors.account).click() - - const accountAddressInClipboard = await metamaskPage.evaluate('navigator.clipboard.readText()') - expect(accountAddressInClipboard).toContain('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266') + await expect(metamaskPage.getByText('Account 1')).toBeVisible() + await expect(metamaskPage.getByText('0xf39Fd...92266')).toBeVisible() }) test('should throw an error if there is no account with target name', async ({ context, metamaskPage }) => { diff --git a/wallets/metamask/test/e2e/metamask/switchNetwork.spec.ts b/wallets/metamask/test/e2e/metamask/switchNetwork.spec.ts index b17f1fe8a..b57bfb6ee 100644 --- a/wallets/metamask/test/e2e/metamask/switchNetwork.spec.ts +++ b/wallets/metamask/test/e2e/metamask/switchNetwork.spec.ts @@ -10,16 +10,13 @@ const { expect } = test test('should switch network', async ({ context, metamaskPage }) => { const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword) - await metamask.openSettings() - - const SidebarMenus = metamask.homePage.selectors.settings.SettingsSidebarMenus - await metamask.openSidebarMenu(SidebarMenus.Advanced) - await metamask.toggleShowTestNetworks() + await metamaskPage.locator(metamask.homePage.selectors.networkDropdown.closeDropdownButton).click() + await expect(metamaskPage.locator(metamask.homePage.selectors.currentNetwork)).toHaveText('Ethereum Mainnet') - const targetNetwork = 'Sepolia test network' + const targetNetwork = 'Sepolia' await metamask.switchNetwork(targetNetwork) await expect(metamaskPage.locator(metamask.homePage.selectors.currentNetwork)).toHaveText(targetNetwork) diff --git a/wallets/metamask/test/e2e/metamask/toggleImprovedTokenAllowanceExperience.spec.ts b/wallets/metamask/test/e2e/metamask/toggleDismissSecretRecoveryPhraseReminder.spec.ts similarity index 60% rename from wallets/metamask/test/e2e/metamask/toggleImprovedTokenAllowanceExperience.spec.ts rename to wallets/metamask/test/e2e/metamask/toggleDismissSecretRecoveryPhraseReminder.spec.ts index 1806cae30..75edca1e5 100644 --- a/wallets/metamask/test/e2e/metamask/toggleImprovedTokenAllowanceExperience.spec.ts +++ b/wallets/metamask/test/e2e/metamask/toggleDismissSecretRecoveryPhraseReminder.spec.ts @@ -8,18 +8,18 @@ const test = testWithSynpress(basicSetup, unlockForFixture) const { expect } = test -test('should toggle the "Improved token allowance experience" option', async ({ context, metamaskPage }) => { +test('should toggle the "Dismiss Secret Recovery Phrase backup reminder" option', async ({ context, metamaskPage }) => { const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword) await metamask.openSettings() const SidebarMenus = metamask.homePage.selectors.settings.SettingsSidebarMenus - await metamask.openSidebarMenu(SidebarMenus.Experimental) + await metamask.openSidebarMenu(SidebarMenus.Advanced) - await metamask.toggleImprovedTokenAllowanceExperience() + await metamask.toggleDismissSecretRecoveryPhraseReminder() // We have to wait for the toggle to be "toggled". This is a hacky workaround, unfortunately. - await expect( - metamaskPage.locator(Selectors.settings.experimental.toggleImprovedTokenAllowanceExperience) - ).toHaveClass(/toggle-button--on/) + await expect(metamaskPage.locator(Selectors.settings.advanced.dismissSecretRecoveryPhraseReminderToggle)).toHaveClass( + /toggle-button--on/ + ) }) diff --git a/wallets/metamask/test/e2e/metamask/toggleShowTestNetworks.spec.ts b/wallets/metamask/test/e2e/metamask/toggleShowTestNetworks.spec.ts index 586763eec..d13da5d08 100644 --- a/wallets/metamask/test/e2e/metamask/toggleShowTestNetworks.spec.ts +++ b/wallets/metamask/test/e2e/metamask/toggleShowTestNetworks.spec.ts @@ -8,29 +8,19 @@ const test = testWithSynpress(basicSetup, unlockForFixture) const { expect } = test -test('should toggle the "Show test networks" option', async ({ context, metamaskPage }) => { +test('should toggle the "Show test networks" option from the networks dropdown', async ({ context, metamaskPage }) => { const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword) await metamaskPage.locator(metamask.homePage.selectors.networkDropdown.dropdownButton).click() const networksCountBefore = await metamaskPage.locator(metamask.homePage.selectors.networkDropdown.networks).count() - // We're closing the network dropdown with this. - await metamask.goBackToHomePage() - - await metamask.openSettings() - - const SidebarMenus = metamask.homePage.selectors.settings.SettingsSidebarMenus - await metamask.openSidebarMenu(SidebarMenus.Advanced) + await metamaskPage.locator(metamask.homePage.selectors.networkDropdown.closeDropdownButton).click() await metamask.toggleShowTestNetworks() // We have to wait for the toggle to be "toggled". This is a hacky workaround, unfortunately. - await expect(metamaskPage.locator(Selectors.settings.advanced.showTestNetworksToggle).nth(1)).toHaveClass( - /toggle-button--on/ - ) - - await metamaskPage.locator(metamask.homePage.selectors.networkDropdown.dropdownButton).click() + await expect(metamaskPage.locator(Selectors.networkDropdown.showTestNetworksToggle)).toHaveClass(/toggle-button--on/) const networksCountAfter = await metamaskPage.locator(metamask.homePage.selectors.networkDropdown.networks).count() diff --git a/wallets/metamask/test/e2e/wallet-setup/basic.setup.ts b/wallets/metamask/test/e2e/wallet-setup/basic.setup.ts index a64ac5658..ae0df552a 100644 --- a/wallets/metamask/test/e2e/wallet-setup/basic.setup.ts +++ b/wallets/metamask/test/e2e/wallet-setup/basic.setup.ts @@ -1,6 +1,5 @@ import { defineWalletSetup } from 'core' import { MetaMask } from '../../../src' -import { waitFor } from '../../../src/utils/waitFor' const SEED_PHRASE = 'test test test test test test test test test test test junk' @@ -10,11 +9,4 @@ export default defineWalletSetup(PASSWORD, async (context, walletPage) => { const metamask = new MetaMask(context, walletPage, PASSWORD) await metamask.importWallet(SEED_PHRASE) - - const recoveryPhraseReminder = walletPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton) - - const isRecoveryPhraseReminderVisible = await waitFor(() => recoveryPhraseReminder.isVisible(), 3_000, false) - if (isRecoveryPhraseReminderVisible) { - await recoveryPhraseReminder.click() - } }) diff --git a/wallets/metamask/test/e2e/wallet-setup/connected.setup.ts b/wallets/metamask/test/e2e/wallet-setup/connected.setup.ts index 0596d9560..e7eb616ce 100644 --- a/wallets/metamask/test/e2e/wallet-setup/connected.setup.ts +++ b/wallets/metamask/test/e2e/wallet-setup/connected.setup.ts @@ -1,7 +1,6 @@ import { defineWalletSetup } from 'core' import { getExtensionId } from 'fixtures' import { MetaMask } from '../../../src' -import { waitFor } from '../../../src/utils/waitFor' const SEED_PHRASE = 'test test test test test test test test test test test junk' @@ -14,12 +13,12 @@ export default defineWalletSetup(PASSWORD, async (context, walletPage) => { await metamask.importWallet(SEED_PHRASE) - const recoveryPhraseReminder = walletPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton) + await metamask.openSettings() - const isRecoveryPhraseReminderVisible = await waitFor(() => recoveryPhraseReminder.isVisible(), 3_000, false) - if (isRecoveryPhraseReminderVisible) { - await recoveryPhraseReminder.click() - } + const SidebarMenus = metamask.homePage.selectors.settings.SettingsSidebarMenus + await metamask.openSidebarMenu(SidebarMenus.Advanced) + + await metamask.toggleDismissSecretRecoveryPhraseReminder() const page = await context.newPage()