diff --git a/wallets/metamask/src/cypress/MetaMask.ts b/wallets/metamask/src/cypress/MetaMask.ts index 00b9668b..6bc39eaa 100644 --- a/wallets/metamask/src/cypress/MetaMask.ts +++ b/wallets/metamask/src/cypress/MetaMask.ts @@ -1,11 +1,258 @@ -import { lockPage } from '../selectors' -import { MetaMaskAbstract } from '../type/MetaMaskAbstract' - -// @ts-ignore -// TODO: To be implemented -export class MetaMask extends MetaMaskAbstract { - unlock() { - cy.get(lockPage.passwordInput).type(this.password) - cy.get(lockPage.submitButton).click() +import { type BrowserContext, type Page, expect } from '@playwright/test' +import { type CreateAnvilOptions, type Pool, createPool } from '@viem/anvil' +import { MetaMask as MetaMaskPlaywright } from '../playwright/MetaMask' +import { waitFor } from '../playwright/utils/waitFor' +import HomePageSelectors from '../selectors/pages/HomePage' +import Selectors from '../selectors/pages/HomePage' +import type { Network } from '../type/Network' +import getPlaywrightMetamask from './getPlaywrightMetamask' + +let pool: Pool + +export default class MetaMask { + readonly metamaskPlaywright: MetaMaskPlaywright + readonly metamaskExtensionPage: Page + + constructor(context: BrowserContext, metamaskExtensionPage: Page, metamaskExtensionId: string) { + this.metamaskPlaywright = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) + this.metamaskExtensionPage = metamaskExtensionPage + } + + async getAccount() { + return await this.metamaskExtensionPage + .locator(this.metamaskPlaywright.homePage.selectors.accountMenu.accountButton) + .innerText() + } + + async getNetwork() { + return await this.metamaskExtensionPage + .locator(this.metamaskPlaywright.homePage.selectors.currentNetwork) + .innerText() + } + + async connectToDapp(accounts?: string[]) { + return this.metamaskPlaywright + .connectToDapp(accounts) + .then(() => true) + .catch(() => false) + } + + async addNewAccount(accountName: string) { + await this.metamaskPlaywright.addNewAccount(accountName) + + await expect( + this.metamaskExtensionPage.locator(this.metamaskPlaywright.homePage.selectors.accountMenu.accountButton) + ).toHaveText(accountName) + + return true + } + + async switchAccount(accountName: string) { + await this.metamaskPlaywright.switchAccount(accountName) + + await expect( + this.metamaskExtensionPage.locator(this.metamaskPlaywright.homePage.selectors.accountMenu.accountButton) + ).toHaveText(accountName) + + return true + } + + async renameAccount({ + currentAccountName, + newAccountName + }: { + currentAccountName: string + newAccountName: string + }) { + await this.metamaskPlaywright.renameAccount(currentAccountName, newAccountName) + + await this.metamaskExtensionPage.locator(HomePageSelectors.threeDotsMenu.accountDetailsCloseButton).click() + + await expect( + this.metamaskExtensionPage.locator(this.metamaskPlaywright.homePage.selectors.accountMenu.accountButton) + ).toHaveText(newAccountName) + + return true + } + + async switchNetwork({ + networkName, + isTestnet = false + }: { + networkName: string + isTestnet?: boolean + }) { + return await this.metamaskPlaywright + .switchNetwork(networkName, isTestnet) + .then(() => { + return true + }) + .catch(() => { + return false + }) + } + + async createAnvilNode(options?: CreateAnvilOptions) { + pool = createPool() + + const nodeId = Array.from(pool.instances()).length + const anvil = await pool.start(nodeId, options) + + const rpcUrl = `http://${anvil.host}:${anvil.port}` + + const DEFAULT_ANVIL_CHAIN_ID = 31337 + const chainId = options?.chainId ?? DEFAULT_ANVIL_CHAIN_ID + + return { anvil, rpcUrl, chainId } + } + + async emptyAnvilNode() { + await pool.empty() + return true + } + + async connectToAnvil({ + rpcUrl, + chainId + }: { + rpcUrl: string + chainId: number + }) { + try { + await this.metamaskPlaywright.addNetwork({ + name: 'Anvil', + rpcUrl, + chainId, + symbol: 'ETH', + blockExplorerUrl: 'https://etherscan.io/' + }) + + await this.metamaskPlaywright.switchNetwork('Anvil') + return true + } catch (e) { + console.error('Error connecting to Anvil network', e) + return false + } + } + + async addNetwork(network: Network) { + await this.metamaskPlaywright.addNetwork(network) + + await waitFor( + () => this.metamaskExtensionPage.locator(HomePageSelectors.networkAddedPopover.switchToNetworkButton).isVisible(), + 3_000, + false + ) + + await this.metamaskExtensionPage.locator(HomePageSelectors.networkAddedPopover.switchToNetworkButton).click() + + return true + } + + // Token + + async deployToken() { + await this.metamaskPlaywright.confirmTransaction() + + return true + } + + async addNewToken() { + await this.metamaskPlaywright.addNewToken() + + await expect(this.metamaskExtensionPage.locator(Selectors.portfolio.singleToken).nth(1)).toContainText('TST') + + return true + } + + async approveNewNetwork() { + await this.metamaskPlaywright.approveNewNetwork() + + return true + } + + async approveSwitchNetwork() { + await this.metamaskPlaywright.approveSwitchNetwork() + + return true + } + + // Others + + async providePublicEncryptionKey() { + return await this.metamaskPlaywright + .providePublicEncryptionKey() + .then(() => { + return true + }) + .catch(() => { + return false + }) + } + + async decrypt() { + return await this.metamaskPlaywright + .decrypt() + .then(() => { + return true + }) + .catch(() => { + return false + }) + } + + async confirmSignature() { + return await this.metamaskPlaywright + .confirmSignature() + .then(() => { + return true + }) + .catch(() => { + return false + }) + } + + async confirmTransaction() { + return await this.metamaskPlaywright + .confirmTransaction() + .then(() => { + return true + }) + .catch(() => { + return false + }) + } + + async confirmTransactionAndWaitForMining() { + return this.metamaskPlaywright + .confirmTransactionAndWaitForMining() + .then(() => { + return true + }) + .catch(() => { + return false + }) + } + + async openTransactionDetails(txIndex: number) { + return this.metamaskPlaywright + .openTransactionDetails(txIndex) + .then(() => { + return true + }) + .catch(() => { + return false + }) + } + + async closeTransactionDetails() { + return this.metamaskPlaywright + .closeTransactionDetails() + .then(() => { + return true + }) + .catch(() => { + return false + }) } } diff --git a/wallets/metamask/src/cypress/configureSynpress.ts b/wallets/metamask/src/cypress/configureSynpress.ts index b2df1709..8549ce60 100644 --- a/wallets/metamask/src/cypress/configureSynpress.ts +++ b/wallets/metamask/src/cypress/configureSynpress.ts @@ -1,16 +1,12 @@ import type { BrowserContext, Page } from '@playwright/test' -import { expect } from '@playwright/test' import { ensureRdpPort } from '@synthetixio/synpress-core' -import { type CreateAnvilOptions, type Pool, createPool } from '@viem/anvil' -import { waitFor } from '../playwright/utils/waitFor' -import HomePageSelectors from '../selectors/pages/HomePage' -import Selectors from '../selectors/pages/HomePage' +import type { CreateAnvilOptions } from '@viem/anvil' import type { Network } from '../type/Network' -import getPlaywrightMetamask from './getPlaywrightMetamask' +import MetaMask from './MetaMask' import importMetaMaskWallet from './support/importMetaMaskWallet' import { initMetaMask } from './support/initMetaMask' -let metamaskInitialized = false +let metamask: MetaMask let rdpPort: number @@ -19,8 +15,6 @@ let metamaskExtensionId: string let metamaskExtensionPage: Page -let pool: Pool - // TODO: Implement if needed to change the focus between pages // let cypressPage: Page @@ -46,7 +40,7 @@ export default function configureSynpress(on: Cypress.PluginEvents, config: Cypr }) on('before:spec', async () => { - if (!metamaskInitialized) { + if (!metamask) { const { context: _context, metamaskExtensionId: _metamaskExtensionId, @@ -62,259 +56,61 @@ export default function configureSynpress(on: Cypress.PluginEvents, config: Cypr // if (_cypressPage) { // cypressPage = _cypressPage // } - metamaskInitialized = true + metamask = new MetaMask(context, metamaskExtensionPage, metamaskExtensionId) } }) on('task', { // Synpress API - async getAccount() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return await metamaskExtensionPage.locator(metamask.homePage.selectors.accountMenu.accountButton).innerText() - }, - - async getNetwork() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return await metamaskExtensionPage.locator(metamask.homePage.selectors.currentNetwork).innerText() - }, - - async connectToDapp(accounts?: string[]) { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return metamask - .connectToDapp(accounts) - .then(() => true) - .catch(() => false) - }, - - async addNewAccount(accountName: string) { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.addNewAccount(accountName) - await expect(metamaskExtensionPage.locator(metamask.homePage.selectors.accountMenu.accountButton)).toHaveText( - accountName - ) - - return true - }, - - async switchAccount(accountName: string) { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.switchAccount(accountName) - - await expect(metamaskExtensionPage.locator(metamask.homePage.selectors.accountMenu.accountButton)).toHaveText( - accountName - ) - - return true - }, - - async renameAccount({ + // Account + connectToDapp: () => metamask?.connectToDapp(), + getAccount: () => metamask?.getAccount(), + addNewAccount: (accountName: string) => metamask?.addNewAccount(accountName), + switchAccount: (accountName: string) => metamask?.switchAccount(accountName), + renameAccount: ({ currentAccountName, newAccountName }: { currentAccountName: string newAccountName: string - }) { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.renameAccount(currentAccountName, newAccountName) + }) => metamask?.renameAccount({ currentAccountName, newAccountName }), - await metamaskExtensionPage.locator(HomePageSelectors.threeDotsMenu.accountDetailsCloseButton).click() - - await expect(metamaskExtensionPage.locator(metamask.homePage.selectors.accountMenu.accountButton)).toHaveText( - newAccountName - ) - - return true - }, - - async switchNetwork({ + // Network + getNetwork: () => metamask?.getNetwork(), + switchNetwork: ({ networkName, - isTestnet + isTestnet = false }: { networkName: string isTestnet?: boolean - }) { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return await metamask - .switchNetwork(networkName, isTestnet) - .then(() => { - return true - }) - .catch(() => { - return false - }) - }, - - async createAnvilNode(options?: CreateAnvilOptions) { - pool = createPool() - - const nodeId = Array.from(pool.instances()).length - const anvil = await pool.start(nodeId, options) - - const rpcUrl = `http://${anvil.host}:${anvil.port}` - - const DEFAULT_ANVIL_CHAIN_ID = 31337 - const chainId = options?.chainId ?? DEFAULT_ANVIL_CHAIN_ID - - return { anvil, rpcUrl, chainId } - }, - - async emptyAnvilNode() { - await pool.empty() - return true - }, - - async connectToAnvil({ - rpcUrl, - chainId - }: { - rpcUrl: string - chainId: number - }) { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - try { - await metamask.addNetwork({ - name: 'Anvil', - rpcUrl, - chainId, - symbol: 'ETH', - blockExplorerUrl: 'https://etherscan.io/' - }) - - await metamask.switchNetwork('Anvil') - return true - } catch (e) { - console.error('Error connecting to Anvil network', e) - return false - } - }, - - async addNetwork(network: Network) { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.addNetwork(network) - - await waitFor( - () => metamaskExtensionPage.locator(HomePageSelectors.networkAddedPopover.switchToNetworkButton).isVisible(), - 3_000, - false - ) - - await metamaskExtensionPage.locator(HomePageSelectors.networkAddedPopover.switchToNetworkButton).click() - - return true - }, + }) => + metamask?.switchNetwork({ + networkName, + isTestnet + }), + addNetwork: (network: Network) => metamask?.addNetwork(network), + approveNewNetwork: () => metamask?.approveNewNetwork(), + approveSwitchNetwork: () => metamask?.approveSwitchNetwork(), + + // Anvil + createAnvilNode: (options?: CreateAnvilOptions) => metamask?.createAnvilNode(options), + emptyAnvilNode: () => metamask?.emptyAnvilNode(), // Token - - async deployToken() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.confirmTransaction() - - return true - }, - - async addNewToken() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.addNewToken() - - await expect(metamaskExtensionPage.locator(Selectors.portfolio.singleToken).nth(1)).toContainText('TST') - - return true - }, - - async approveNewNetwork() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.approveNewNetwork() - - return true - }, - - async approveSwitchNetwork() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - await metamask.approveSwitchNetwork() - - return true - }, - - // Others - - async providePublicEncryptionKey() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return await metamask - .providePublicEncryptionKey() - .then(() => { - return true - }) - .catch(() => { - return false - }) - }, - - async decrypt() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return await metamask - .decrypt() - .then(() => { - return true - }) - .catch(() => { - return false - }) - }, - - async confirmSignature() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return await metamask - .confirmSignature() - .then(() => { - return true - }) - .catch(() => { - return false - }) - }, - - async confirmTransaction() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return await metamask - .confirmTransaction() - .then(() => { - return true - }) - .catch(() => { - return false - }) - }, - - async confirmTransactionAndWaitForMining() { - const metamask = getPlaywrightMetamask(context, metamaskExtensionPage, metamaskExtensionId) - - return metamask - .confirmTransactionAndWaitForMining() - .then(() => { - return true - }) - .catch(() => { - return false - }) - } + deployToken: () => metamask?.deployToken(), + addNewToken: () => metamask?.addNewToken(), + + // Encryption + providePublicEncryptionKey: () => metamask?.providePublicEncryptionKey(), + decrypt: () => metamask?.decrypt(), + + // Transactions + confirmSignature: () => metamask?.confirmSignature(), + confirmTransaction: () => metamask?.confirmTransaction(), + confirmTransactionAndWaitForMining: () => metamask?.confirmTransactionAndWaitForMining(), + openTransactionDetails: (txIndex: number) => metamask?.openTransactionDetails(txIndex), + closeTransactionDetails: () => metamask?.closeTransactionDetails() }) return { diff --git a/wallets/metamask/src/cypress/support/synpressCommands.ts b/wallets/metamask/src/cypress/support/synpressCommands.ts index 7dcb3e82..39f87436 100644 --- a/wallets/metamask/src/cypress/support/synpressCommands.ts +++ b/wallets/metamask/src/cypress/support/synpressCommands.ts @@ -44,6 +44,8 @@ declare global { confirmSignature(): Chainable confirmTransaction(): Chainable confirmTransactionAndWaitForMining(): Chainable + openTransactionDetails(txIndex: number): Chainable + closeTransactionDetails(): Chainable } } } @@ -138,4 +140,10 @@ export default function synpressCommands() { Cypress.Commands.add('confirmTransactionAndWaitForMining', () => { return cy.task('confirmTransactionAndWaitForMining') }) + Cypress.Commands.add('openTransactionDetails', (txIndex = 0) => { + return cy.task('openTransactionDetails', txIndex) + }) + Cypress.Commands.add('closeTransactionDetails', () => { + return cy.task('closeTransactionDetails') + }) } diff --git a/wallets/metamask/test/cypress/approveNewNetwork.cy.ts b/wallets/metamask/test/cypress/approveNewNetwork.cy.ts index dcf46a38..81becd92 100644 --- a/wallets/metamask/test/cypress/approveNewNetwork.cy.ts +++ b/wallets/metamask/test/cypress/approveNewNetwork.cy.ts @@ -14,3 +14,7 @@ it('should add a new network', () => { }) }) }) + +after(() => { + cy.switchNetwork('Ethereum Mainnet') +})