diff --git a/docs/api/classes/MetaMask.md b/docs/api/classes/MetaMask.md index 3051ef60d..9297d4a42 100644 --- a/docs/api/classes/MetaMask.md +++ b/docs/api/classes/MetaMask.md @@ -216,10 +216,16 @@ Confirms a transaction request. ### connectToDapp() ```ts -connectToDapp(): Promise +connectToDapp(accounts?): Promise ``` -Connects to the dapp using the currently selected account. +Connects to the dapp using the currently selected accounts. + +#### Parameters + +| Parameter | Type | Description | +|:-----------|:-----------|:---------------| +| `accounts` | `string[]` | Accounts list. | #### Returns diff --git a/docs/docs/guides/playwright.md b/docs/docs/guides/playwright.md index 331b58db6..10d2040c4 100644 --- a/docs/docs/guides/playwright.md +++ b/docs/docs/guides/playwright.md @@ -113,6 +113,23 @@ test('should connect wallet to dapp', async ({ context, page, extensionId, metam await expect(page.locator('#accounts')).toHaveText('0xdeadbeef') }) ``` +```typescript [basic.spec.ts] +import { testWithMetaMask as test } from './testWithMetaMask'; + +const { expect } = test; + +// The `MetaMask` instance is now available in the test context. +test('should connect multiple wallets to dapp', async ({ context, page, extensionId, metamask }) => { + await page.goto('/'); + + await page.locator('#connectButton').click(); + + await metamask.connectToDapp(['0xdeadbeef1', '0xdeadbeef2']); + + await expect(page.locator('#accounts')).toHaveText('0xdeadbeef1,0xdeadbeef2'); + +}); +``` ::: diff --git a/wallets/metamask/src/metamask.ts b/wallets/metamask/src/metamask.ts index 62701875a..6b2b52115 100644 --- a/wallets/metamask/src/metamask.ts +++ b/wallets/metamask/src/metamask.ts @@ -139,12 +139,12 @@ export class MetaMask { /** * Connects to the dapp using the currently selected account. */ - async connectToDapp() { + async connectToDapp(accounts?: string[]) { if (!this.extensionId) { throw NO_EXTENSION_ID_ERROR } - await this.notificationPage.connectToDapp(this.extensionId) + await this.notificationPage.connectToDapp(this.extensionId, accounts) } /** diff --git a/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts b/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts index 9b8bf6d48..59e38a840 100644 --- a/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts +++ b/wallets/metamask/src/pages/NotificationPage/actions/connectToDapp.ts @@ -1,10 +1,36 @@ -import type { Page } from '@playwright/test' +import type { Locator, Page } from '@playwright/test' import Selectors from '../selectors' +import { allTextContents } from '../../../utils/allTextContents' -export async function connectToDapp(notificationPage: Page) { - // Click `Next`. - await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() +async function selectAccounts(accountsToSelect: string[], accountLocators: Locator[], availableAccountNames: string[]) { + for (const account of accountsToSelect) { + const accountNameIndex = availableAccountNames.findIndex((name) => name.startsWith(account)) + if (accountNameIndex < 0) throw new Error(`[ConnectToDapp] Account with name ${account} not found`) + await accountLocators[accountNameIndex]?.locator(Selectors.ConnectPage.accountCheckbox).check() + } +} - // Click `Connect`. +async function confirmConnection(notificationPage: Page) { await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() + await notificationPage.locator(Selectors.ActionFooter.confirmActionButton).click() +} + +// By default, only the last account will be selected. If you want to select a specific account, pass `accounts` parameter. +export async function connectToDapp(notificationPage: Page, accounts?: string[]) { + if (accounts && accounts.length > 0) { + // Wait for the accounts to be loaded as 'all()' doesnt not wait for the results - https://playwright.dev/docs/api/class-locator#locator-all + // Additionally disable default account to reuse necessary delay + await notificationPage + .locator(Selectors.ConnectPage.accountOption) + .locator(Selectors.ConnectPage.accountCheckbox) + .last() + .setChecked(false) + + const accountLocators = await notificationPage.locator(Selectors.ConnectPage.accountOption).all() + const accountNames = await allTextContents(accountLocators) + + await selectAccounts(accounts, accountLocators, accountNames) + } + + await confirmConnection(notificationPage) } diff --git a/wallets/metamask/src/pages/NotificationPage/page.ts b/wallets/metamask/src/pages/NotificationPage/page.ts index 33ee8bfbb..dab7a4a0e 100644 --- a/wallets/metamask/src/pages/NotificationPage/page.ts +++ b/wallets/metamask/src/pages/NotificationPage/page.ts @@ -22,10 +22,10 @@ export class NotificationPage { this.page = page } - async connectToDapp(extensionId: string) { + async connectToDapp(extensionId: string, accounts?: string[]) { const notificationPage = await getNotificationPageAndWaitForLoad(this.page.context(), extensionId) - await connectToDapp(notificationPage) + await connectToDapp(notificationPage, accounts) } // TODO: Revisit this logic in the future to see if we can increase the performance by utilizing `Promise.race`. diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/connectPage.ts b/wallets/metamask/src/pages/NotificationPage/selectors/connectPage.ts new file mode 100644 index 000000000..3e50f4349 --- /dev/null +++ b/wallets/metamask/src/pages/NotificationPage/selectors/connectPage.ts @@ -0,0 +1,4 @@ +export default { + accountOption: '.choose-account-list .choose-account-list__list .choose-account-list__account', + accountCheckbox: 'input.choose-account-list__list-check-box' +} diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/index.ts b/wallets/metamask/src/pages/NotificationPage/selectors/index.ts index a8c200460..66267cd73 100644 --- a/wallets/metamask/src/pages/NotificationPage/selectors/index.ts +++ b/wallets/metamask/src/pages/NotificationPage/selectors/index.ts @@ -1,4 +1,5 @@ import ActionFooter from './actionFooter' +import ConnectPage from './connectPage' import NetworkPage from './networkPage' import PermissionPage from './permissionPage' import SignaturePage from './signaturePage' @@ -6,6 +7,7 @@ import TransactionPage from './transactionPage' export default { ActionFooter, + ConnectPage, SignaturePage, NetworkPage, PermissionPage, diff --git a/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts b/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts index 3c983dde7..5175cccc4 100644 --- a/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts +++ b/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts @@ -18,3 +18,20 @@ test('should connect wallet to dapp', async ({ context, page, extensionId }) => await expect(page.locator('#accounts')).toHaveText('0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266') }) + +test('should connect multiple wallets to dapp', async ({ context, page, metamaskPage, extensionId }) => { + const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword, extensionId) + + await metamask.addNewAccount('Account 2') + await metamask.addNewAccount('Account 3') + + await page.goto('/') + await page.locator('#connectButton').click() + + // "accounts" param is order agnostic + await metamask.connectToDapp(['Account 2', 'Account 1']) + + await expect(page.locator('#accounts')).toHaveText( + '0x70997970c51812dc3a010c7d01b50e0d17dc79c8,0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' + ) +})