diff --git a/wallets/metamask/package.json b/wallets/metamask/package.json index 4a6b38e2e..bf6df6c42 100644 --- a/wallets/metamask/package.json +++ b/wallets/metamask/package.json @@ -28,6 +28,7 @@ "test:coverage": "vitest run --coverage", "test:e2e:headful": "playwright test", "test:e2e:headless": "HEADLESS=true playwright test", + "test:e2e:headless:ui": "HEADLESS=true playwright test --ui", "test:watch": "vitest watch", "types:check": "tsc --noEmit" }, diff --git a/wallets/metamask/src/metamask.ts b/wallets/metamask/src/metamask.ts index ee0d193b1..f0b70eddf 100644 --- a/wallets/metamask/src/metamask.ts +++ b/wallets/metamask/src/metamask.ts @@ -1,6 +1,8 @@ import type { BrowserContext, Page } from '@playwright/test' import { CrashPage, HomePage, LockPage, NotificationPage, OnboardingPage } from './pages' +const NO_EXTENSION_ID_ERROR = new Error('MetaMask extensionId is not set') + export class MetaMask { crashPage: CrashPage onboardingPage: OnboardingPage @@ -8,7 +10,12 @@ export class MetaMask { homePage: HomePage notificationPage: NotificationPage - constructor(readonly context: BrowserContext, readonly page: Page, readonly password: string) { + constructor( + readonly context: BrowserContext, + readonly page: Page, + readonly password: string, + readonly extensionId?: string + ) { this.crashPage = new CrashPage() this.onboardingPage = new OnboardingPage(page) @@ -21,8 +28,11 @@ export class MetaMask { await this.onboardingPage.importWallet(seedPhrase, this.password) } - async connectToDapp(extensionId: string) { - await this.notificationPage.connectToDapp(extensionId) + async connectToDapp() { + if (!this.extensionId) { + throw NO_EXTENSION_ID_ERROR + } + await this.notificationPage.connectToDapp(this.extensionId) } async lock() { @@ -32,4 +42,18 @@ export class MetaMask { async unlock() { await this.lockPage.unlock(this.password) } + + async confirmSignature() { + if (!this.extensionId) { + throw NO_EXTENSION_ID_ERROR + } + await this.notificationPage.signPersonalMessage(this.extensionId) + } + + async rejectSignature() { + if (!this.extensionId) { + throw NO_EXTENSION_ID_ERROR + } + await this.notificationPage.rejectPersonalMessage(this.extensionId) + } } diff --git a/wallets/metamask/src/pages/NotificationPage/actions/index.ts b/wallets/metamask/src/pages/NotificationPage/actions/index.ts index 0b0b193ac..32887b91c 100644 --- a/wallets/metamask/src/pages/NotificationPage/actions/index.ts +++ b/wallets/metamask/src/pages/NotificationPage/actions/index.ts @@ -1 +1,2 @@ export * from './connectToDapp' +export * from './personalSign' diff --git a/wallets/metamask/src/pages/NotificationPage/actions/personalSign.ts b/wallets/metamask/src/pages/NotificationPage/actions/personalSign.ts new file mode 100644 index 000000000..c865a1d5d --- /dev/null +++ b/wallets/metamask/src/pages/NotificationPage/actions/personalSign.ts @@ -0,0 +1,20 @@ +import type { BrowserContext } from '@playwright/test' +import { getNotificationPage } from '../../../utils/getNotificationPage' +import Selectors from '../selectors' + +const signPersonalMessage = async (context: BrowserContext, extensionId: string) => { + const notificationPage = await getNotificationPage(context, extensionId) + + await notificationPage.locator(Selectors.SignaturePage.signButton).click() +} + +const rejectPersonalMessage = async (context: BrowserContext, extensionId: string) => { + const notificationPage = await getNotificationPage(context, extensionId) + + await notificationPage.locator(Selectors.SignaturePage.rejectButton).click() +} + +export const personalSign = { + sign: signPersonalMessage, + reject: rejectPersonalMessage +} diff --git a/wallets/metamask/src/pages/NotificationPage/page.ts b/wallets/metamask/src/pages/NotificationPage/page.ts index 33be594b5..2952b0a38 100644 --- a/wallets/metamask/src/pages/NotificationPage/page.ts +++ b/wallets/metamask/src/pages/NotificationPage/page.ts @@ -1,5 +1,5 @@ import type { Page } from '@playwright/test' -import { connectToDapp } from './actions' +import { connectToDapp, personalSign } from './actions' export class NotificationPage { readonly page: Page @@ -11,4 +11,12 @@ export class NotificationPage { async connectToDapp(extensionId: string) { await connectToDapp(this.page.context(), extensionId) } + + async signPersonalMessage(extensionId: string) { + await personalSign.sign(this.page.context(), extensionId) + } + + async rejectPersonalMessage(extensionId: string) { + await personalSign.reject(this.page.context(), extensionId) + } } diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/index.ts b/wallets/metamask/src/pages/NotificationPage/selectors/index.ts new file mode 100644 index 000000000..b04852dc5 --- /dev/null +++ b/wallets/metamask/src/pages/NotificationPage/selectors/index.ts @@ -0,0 +1,5 @@ +import SignaturePage from './signaturePage' + +export default { + SignaturePage +} diff --git a/wallets/metamask/src/pages/NotificationPage/selectors/signaturePage.ts b/wallets/metamask/src/pages/NotificationPage/selectors/signaturePage.ts new file mode 100644 index 000000000..2f2879088 --- /dev/null +++ b/wallets/metamask/src/pages/NotificationPage/selectors/signaturePage.ts @@ -0,0 +1,6 @@ +import { createDataTestSelector } from '../../../utils/selectors/createDataTestSelector' + +export default { + signButton: `.request-signature__footer ${createDataTestSelector('request-signature__sign')}`, + rejectButton: '.request-signature__footer button.btn-secondary' +} diff --git a/wallets/metamask/test/e2e/metamask/confirmSignature.spec.ts b/wallets/metamask/test/e2e/metamask/confirmSignature.spec.ts new file mode 100644 index 000000000..310e57782 --- /dev/null +++ b/wallets/metamask/test/e2e/metamask/confirmSignature.spec.ts @@ -0,0 +1,33 @@ +import { testWithSynpress } from 'fixtures' +import { MetaMask, unlockForFixture } from '../../../src' + +import connectedSetup from '../wallet-setup/connected.setup' + +const test = testWithSynpress(connectedSetup, unlockForFixture) + +const { describe, expect } = test + +describe('MetaMask.confirmSignature', () => { + test('should confirm `personal_sign`', async ({ context, metamaskPage, page, extensionId }) => { + const metamask = new MetaMask(context, metamaskPage, connectedSetup.walletPassword, extensionId) + + await page.goto('https://metamask.github.io/test-dapp/') + + await page.locator('#personalSign').click() + + await metamask.confirmSignature() + + await expect(page.locator('#personalSignResult')).toHaveText( + '0xf95b3efc808585303e20573e960993cde30c7f5a0f1c25cfab0379d5a14311d17898199814c8ebe66ec80b2b11690f840bde539f862ff4f04468d2a40f15178a1b' + ) + + await page.locator('#personalSignVerify').click() + + await expect(page.locator('#personalSignVerifySigUtilResult')).toHaveText( + '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' + ) + await expect(page.locator('#personalSignVerifyECRecoverResult')).toHaveText( + '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' + ) + }) +}) diff --git a/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts b/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts index c306cfb6d..600ee3e3b 100644 --- a/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts +++ b/wallets/metamask/test/e2e/metamask/connectToDapp.spec.ts @@ -9,13 +9,13 @@ const { describe, expect } = test describe('MetaMask.connectToDapp', () => { test('should connect wallet to dapp', async ({ context, page, extensionId }) => { - const metamask = new MetaMask(context, page, basicSetup.walletPassword) + const metamask = new MetaMask(context, page, basicSetup.walletPassword, extensionId) await page.goto('https://metamask.github.io/test-dapp/') await page.locator('#connectButton').click() - await metamask.connectToDapp(extensionId) + await metamask.connectToDapp() await expect(page.locator('#accounts')).toHaveText('0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266') }) diff --git a/wallets/metamask/test/e2e/metamask/rejectSignature.spec.ts b/wallets/metamask/test/e2e/metamask/rejectSignature.spec.ts new file mode 100644 index 000000000..fa4a7531a --- /dev/null +++ b/wallets/metamask/test/e2e/metamask/rejectSignature.spec.ts @@ -0,0 +1,25 @@ +import { testWithSynpress } from 'fixtures' +import { MetaMask, unlockForFixture } from '../../../src' + +import connectedSetup from '../wallet-setup/connected.setup' + +const test = testWithSynpress(connectedSetup, unlockForFixture) + +const { describe, expect } = test + +describe('MetaMask.rejectSignature', () => { + test('should reject `personal_sign`', async ({ context, metamaskPage, page, extensionId }) => { + const metamask = new MetaMask(context, metamaskPage, connectedSetup.walletPassword, extensionId) + + await page.goto('https://metamask.github.io/test-dapp/') + + await page.locator('#personalSign').click() + + await metamask.rejectSignature() + + await expect(page.locator('#personalSign')).toHaveText( + 'Error: MetaMask Message Signature: User denied message signature.' + ) + await expect(page.locator('#personalSignResult')).toHaveText('') + }) +}) diff --git a/wallets/metamask/test/e2e/wallet-setup/connected.setup.ts b/wallets/metamask/test/e2e/wallet-setup/connected.setup.ts new file mode 100644 index 000000000..002bd3614 --- /dev/null +++ b/wallets/metamask/test/e2e/wallet-setup/connected.setup.ts @@ -0,0 +1,25 @@ +import { defineWalletSetup } from 'core' +import { getExtensionId } from 'fixtures' +import { MetaMask } from '../../../src' + +const SEED_PHRASE = 'test test test test test test test test test test test junk' + +const PASSWORD = 'Tester@1234' + +export default defineWalletSetup(PASSWORD, async (context, walletPage) => { + const extensionId = await getExtensionId(context, 'MetaMask') + + const metamask = new MetaMask(context, walletPage, PASSWORD, extensionId) + + await metamask.importWallet(SEED_PHRASE) + + const page = await context.newPage() + + await page.goto('https://metamask.github.io/test-dapp/') + + await page.locator('#connectButton').click() + + await metamask.connectToDapp() + + await page.close() +})