diff --git a/wallets/metamask/src/metamask.ts b/wallets/metamask/src/metamask.ts index f99f334ef..6cca038fe 100644 --- a/wallets/metamask/src/metamask.ts +++ b/wallets/metamask/src/metamask.ts @@ -28,6 +28,10 @@ export class MetaMask { await this.onboardingPage.importWallet(seedPhrase, this.password) } + async importWalletFromPrivateKey(privateKey: string) { + await this.homePage.importWalletFromPrivateKey(privateKey) + } + async connectToDapp() { if (!this.extensionId) { throw NO_EXTENSION_ID_ERROR diff --git a/wallets/metamask/src/pages/HomePage/page.ts b/wallets/metamask/src/pages/HomePage/page.ts index d8b280ee1..3e57c52f8 100644 --- a/wallets/metamask/src/pages/HomePage/page.ts +++ b/wallets/metamask/src/pages/HomePage/page.ts @@ -1,4 +1,5 @@ import type { Page } from '@playwright/test' +import { waitFor } from '../../utils/waitFor' import { lock } from './actions' import Selectors from './selectors' @@ -15,4 +16,24 @@ export class HomePage { async lock() { await lock(this.page) } + + async importWalletFromPrivateKey(privateKey: string) { + await this.page.locator(Selectors.accountMenu.accountMenuButton).click() + await this.page.locator(Selectors.accountMenu.importAccountButton).click() + await this.page.locator(Selectors.importAccountScreen.privateKeyInput).fill(privateKey) + + const importButton = this.page.locator(Selectors.importAccountScreen.importButton) + await importButton.click() + + // TODO: Extract & make configurable + const isHidden = await waitFor(importButton, 'hidden', 1000, false) + + if (!isHidden) { + const errorText = await this.page.locator(Selectors.importAccountScreen.error).textContent({ + timeout: 1000 // TODO: Extract & make configurable + }) + + throw new Error(`[ImportWalletFromPrivateKey] Importing failed due to error: ${errorText}`) + } + } } diff --git a/wallets/metamask/src/pages/HomePage/selectors/index.ts b/wallets/metamask/src/pages/HomePage/selectors/index.ts index 30111cfc7..6bd8eba75 100644 --- a/wallets/metamask/src/pages/HomePage/selectors/index.ts +++ b/wallets/metamask/src/pages/HomePage/selectors/index.ts @@ -3,10 +3,24 @@ import { createDataTestSelector } from '../../../utils/selectors/createDataTestS const container = '.account-menu' const accountMenu = { accountMenuButton: createDataTestSelector('account-menu-icon'), - lockButton: `${container} .account-menu__lock-button` + lockButton: `${container} .account-menu__lock-button`, + importAccountButton: `${container} > .account-menu__item--clickable:nth-of-type(2)` +} + +const importAccountScreen = { + privateKeyInput: 'input#private-key-box', + importButton: '.new-account-import-form__buttons button.btn-primary', + error: 'span.error' +} + +const recoveryPhraseReminder = { + gotItButton: '.recovery-phrase-reminder button.btn-primary' } export default { logo: createDataTestSelector('app-header-logo'), - accountMenu + account: createDataTestSelector('selected-account-click'), + accountMenu, + importAccountScreen, + recoveryPhraseReminder } diff --git a/wallets/metamask/test/e2e/metamask/importWalletFromPrivateKey.spec.ts b/wallets/metamask/test/e2e/metamask/importWalletFromPrivateKey.spec.ts new file mode 100644 index 000000000..bb0310dd9 --- /dev/null +++ b/wallets/metamask/test/e2e/metamask/importWalletFromPrivateKey.spec.ts @@ -0,0 +1,61 @@ +import { testWithSynpress } from 'fixtures' +import { MetaMask, unlockForFixture } from '../../../src' + +import basicSetup from '../wallet-setup/basic.setup' + +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) + + if (await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).isVisible()) { + await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).click() + } + + await metamask.importWalletFromPrivateKey('ea084c575a01e2bbefcca3db101eaeab1d8af15554640a510c73692db24d0a6a') + + await metamaskPage.locator(metamask.homePage.selectors.account).click() + + const accountAddressInClipboard = await metamaskPage.evaluate('navigator.clipboard.readText()') + expect(accountAddressInClipboard).toContain('0xa2ce797cA71d0EaE1be5a7EffD27Fd6C38126801') +}) + +test('should throw an error if trying to import private key for the 2nd time', async ({ context, metamaskPage }) => { + const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword) + + if (await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).isVisible()) { + await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).click() + } + + const privateKey = 'ea084c575a01e2bbefcca3db101eaeab1d8af15554640a510c73692db24d0a6a' + + 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' + ) +}) + +test('should throw an error if the private key is invalid', async ({ context, metamaskPage }) => { + const metamask = new MetaMask(context, metamaskPage, basicSetup.walletPassword) + + if (await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).isVisible()) { + await metamaskPage.locator(metamask.homePage.selectors.recoveryPhraseReminder.gotItButton).click() + } + + const importWalletPromise = metamask.importWalletFromPrivateKey('0xdeadbeef') + + await expect(importWalletPromise).rejects.toThrowError( + '[ImportWalletFromPrivateKey] Importing failed due to error: Expected private key to be an Uint8Array with length 32' + ) +})