diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3892a56806..b112e7e1e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -115,7 +115,7 @@ jobs: - name: Run e2e (Chrome) uses: nick-fields/retry@v2 with: - timeout_minutes: 16 + timeout_minutes: 18 max_attempts: 3 command: | export BROWSER=chrome @@ -173,7 +173,7 @@ jobs: # - name: Run e2e (Brave) # uses: nick-fields/retry@v2 # with: - # timeout_minutes: 16 + # timeout_minutes: 18 # max_attempts: 3 # command: | # export BROWSER=brave diff --git a/e2e/helpers.ts b/e2e/helpers.ts index 4787655b06..e8c12dcea9 100644 --- a/e2e/helpers.ts +++ b/e2e/helpers.ts @@ -66,6 +66,28 @@ export async function getExtensionIdByName(driver, extensionName) { `); } +export function shortenAddress(address) { + // if address is 42 in length and starts with 0x, then shorten it + // otherwise return the base value. this is so it doesn't break incase an ens, etc is input + return address.substring(0, 2) === '0x' && address.length === 42 + ? `${address.substring(0, 6)}...${address.substring(38, 42)}` + : address; +} + +export async function switchWallet(address, rootURL, driver) { + // find shortened address, go to popup, find header, click, find wallet you want to switch to and click + const shortenedAddress = shortenAddress(address); + + await goToPopup(driver, rootURL, '#/home'); + await findElementByIdAndClick({ + id: 'header-account-name-shuffle', + driver, + }); + + await findElementByTextAndClick(driver, shortenedAddress); + await delayTime('short'); +} + export async function getOnchainBalance(addy, contract) { const provider = ethers.getDefaultProvider('http://127.0.0.1:8545'); const testContract = new ethers.Contract(contract, erc20ABI, provider); @@ -94,6 +116,13 @@ export async function findElementByText(driver, text) { return driver.findElement(By.xpath("//*[contains(text(),'" + text + "')]")); } +export async function findElementByTextAndClick(driver, text) { + const element = await driver.findElement( + By.xpath("//*[contains(text(),'" + text + "')]"), + ); + await waitAndClick(element, driver); +} + export async function waitAndClick(element, driver) { await delay(200); await driver.wait(until.elementIsVisible(element), waitUntilTime); @@ -130,6 +159,12 @@ export async function findElementByTestIdAndClick({ id, driver }) { await waitAndClick(element, driver); } +export async function findElementByIdAndClick({ id, driver }) { + await delay(200); + const element = await findElementById({ id, driver }); + await waitAndClick(element, driver); +} + export async function typeOnTextInput({ id, text, driver }) { const element = await findElementByTestId({ id, driver }); await element.sendKeys(text); diff --git a/e2e/parallel/importWalletFlow.test.ts b/e2e/parallel/importWalletFlow.test.ts index e717637f25..11ce691298 100644 --- a/e2e/parallel/importWalletFlow.test.ts +++ b/e2e/parallel/importWalletFlow.test.ts @@ -8,12 +8,12 @@ import { findElementByTestIdAndClick, findElementByText, getExtensionIdByName, + getTextFromText, goToPopup, goToWelcome, initDriverWithOptions, - querySelector, + shortenAddress, typeOnTextInput, - waitAndClick, } from '../helpers'; let rootURL = 'chrome-extension://'; @@ -21,6 +21,7 @@ let driver: WebDriver; const browser = process.env.BROWSER || 'chrome'; const os = process.env.OS || 'mac'; +const wallet = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; describe('Import wallet flow', () => { beforeAll(async () => { @@ -71,31 +72,10 @@ describe('Import wallet flow', () => { await findElementByTestIdAndClick({ id: 'set-password-button', driver }); await delayTime('long'); await findElementByText(driver, 'Your wallets ready'); - }); - - it('should be able to test the sandbox for the popup', async () => { - await goToPopup(driver, rootURL, '#/home'); - await findElementByTestIdAndClick({ id: 'home-page-header-right', driver }); - await findElementByTestIdAndClick({ id: 'settings-link', driver }); - const btn = await querySelector( - driver, - '[data-testid="test-sandbox-popup"]', - ); - await waitAndClick(btn, driver); - const text = await driver.switchTo().alert().getText(); - expect(text).toBe('Popup sandboxed!'); - await driver.switchTo().alert().accept(); - }); - it('should be able to test the sandbox for the background', async () => { - const btn = await querySelector( - driver, - '[data-testid="test-sandbox-background"]', - ); - await waitAndClick(btn, driver); - await delayTime('long'); - const text = await driver.switchTo().alert().getText(); - expect(text).toBe('Background sandboxed!'); - await driver.switchTo().alert().accept(); + goToPopup(driver, rootURL); + await delayTime('short'); + const account = await getTextFromText({ id: 'account-name', driver }); + expect(account).toBe(await shortenAddress(wallet)); }); }); diff --git a/e2e/parallel/importWalletFlowPkey.test.ts b/e2e/parallel/importWalletFlowPkey.test.ts new file mode 100644 index 0000000000..a4d2217af4 --- /dev/null +++ b/e2e/parallel/importWalletFlowPkey.test.ts @@ -0,0 +1,78 @@ +import 'chromedriver'; +import 'geckodriver'; +import { WebDriver } from 'selenium-webdriver'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; + +import { + delayTime, + findElementByTestIdAndClick, + findElementByText, + getExtensionIdByName, + getTextFromText, + goToPopup, + goToWelcome, + initDriverWithOptions, + shortenAddress, + typeOnTextInput, +} from '../helpers'; + +let rootURL = 'chrome-extension://'; +let driver: WebDriver; + +const browser = process.env.BROWSER || 'chrome'; +const os = process.env.OS || 'mac'; +const wallet = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; + +describe('Import wallet flow', () => { + beforeAll(async () => { + driver = await initDriverWithOptions({ + browser, + os, + }); + const extensionId = await getExtensionIdByName(driver, 'Rainbow'); + if (!extensionId) throw new Error('Extension not found'); + rootURL += extensionId; + }); + + afterAll(async () => driver.quit()); + + // Import a wallet + it('should be able import a wallet via pkey', async () => { + // Start from welcome screen + await goToWelcome(driver, rootURL); + await findElementByTestIdAndClick({ + id: 'import-wallet-button', + driver, + }); + await findElementByTestIdAndClick({ + id: 'import-wallet-option', + driver, + }); + + await typeOnTextInput({ + id: 'secret-textarea', + driver, + text: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', + }); + + await findElementByTestIdAndClick({ + id: 'import-wallets-button', + driver, + }); + + await typeOnTextInput({ id: 'password-input', driver, text: 'test1234' }); + await typeOnTextInput({ + id: 'confirm-password-input', + driver, + text: 'test1234', + }); + await findElementByTestIdAndClick({ id: 'set-password-button', driver }); + await delayTime('long'); + await findElementByText(driver, 'Your wallets ready'); + + goToPopup(driver, rootURL); + await delayTime('short'); + const account = await getTextFromText({ id: 'account-name', driver }); + expect(account).toBe(await shortenAddress(wallet)); + }); +}); diff --git a/e2e/parallel/newWalletFlow.test.ts b/e2e/parallel/newWalletFlow.test.ts index fa43e7bf51..ab3db11e6e 100644 --- a/e2e/parallel/newWalletFlow.test.ts +++ b/e2e/parallel/newWalletFlow.test.ts @@ -14,6 +14,7 @@ import { initDriverWithOptions, querySelector, typeOnTextInput, + waitAndClick, } from '../helpers'; let rootURL = 'chrome-extension://'; @@ -96,4 +97,44 @@ describe('New wallet flow', () => { const actual = await label.getText(); expect(actual.substr(0, 2) === '0x' && actual.length === 13).toEqual(true); }); + + it('should be able to lock and unlock the extension', async () => { + await goToPopup(driver, rootURL, '#/home'); + // Lock + await findElementByTestIdAndClick({ + id: 'home-page-header-right', + driver, + }); + await findElementByTestIdAndClick({ id: 'lock', driver }); + + // Unlock + await typeOnTextInput({ id: 'password-input', driver, text: 'test1234' }); + await findElementByTestIdAndClick({ id: 'unlock-button', driver }); + }); + + it('should be able to test the sandbox for the popup', async () => { + await goToPopup(driver, rootURL, '#/home'); + await findElementByTestIdAndClick({ id: 'home-page-header-right', driver }); + await findElementByTestIdAndClick({ id: 'settings-link', driver }); + const btn = await querySelector( + driver, + '[data-testid="test-sandbox-popup"]', + ); + await waitAndClick(btn, driver); + const text = await driver.switchTo().alert().getText(); + expect(text).toBe('Popup sandboxed!'); + await driver.switchTo().alert().accept(); + }); + + it('should be able to test the sandbox for the background', async () => { + const btn = await querySelector( + driver, + '[data-testid="test-sandbox-background"]', + ); + await waitAndClick(btn, driver); + await delayTime('long'); + const text = await driver.switchTo().alert().getText(); + expect(text).toBe('Background sandboxed!'); + await driver.switchTo().alert().accept(); + }); }); diff --git a/e2e/parallel/vitest.config.ts b/e2e/parallel/vitest.config.ts new file mode 100644 index 0000000000..fa6db1a695 --- /dev/null +++ b/e2e/parallel/vitest.config.ts @@ -0,0 +1,13 @@ +import { mergeConfig } from 'vite'; +import { defineConfig } from 'vitest/config'; + +import viteConfig from '../vitest.config'; + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + threads: false, + }, + }), +); diff --git a/e2e/parallel/watchWalletFlow.test.ts b/e2e/parallel/watchWalletFlow.test.ts index 2f6872a220..39437060e4 100644 --- a/e2e/parallel/watchWalletFlow.test.ts +++ b/e2e/parallel/watchWalletFlow.test.ts @@ -5,13 +5,17 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { delayTime, + findElementByIdAndClick, findElementByTestIdAndClick, findElementByText, getExtensionIdByName, + getTextFromText, goToPopup, goToWelcome, initDriverWithOptions, querySelector, + shortenAddress, + switchWallet, typeOnTextInput, } from '../helpers'; @@ -20,8 +24,14 @@ let driver: WebDriver; const browser = process.env.BROWSER || 'chrome'; const os = process.env.OS || 'mac'; +const watchedWallet = 'djweth.eth'; +const watchedWalletTwo = 'brdy.eth'; +const seedWallet = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; +const pkWallet = '0x38eDa688Cd8DFC6FeE8016c85803a584A0564dDC'; +const privateKey = + '0xaeb5635a53c33d3c0d92c32881d7613ee9fe18be77772054f5e025bc4fa8c851'; -describe('Watch wallet flow', () => { +describe('Watch wallet then add more and switch between them', () => { beforeAll(async () => { driver = await initDriverWithOptions({ browser, @@ -50,7 +60,7 @@ describe('Watch wallet flow', () => { await typeOnTextInput({ id: 'secret-textarea', driver, - text: 'djweth.eth', + text: watchedWallet, }); await findElementByTestIdAndClick({ @@ -68,7 +78,7 @@ describe('Watch wallet flow', () => { await findElementByText(driver, 'Your wallets ready'); }); - it('should display account name', async () => { + it('should display watched account name', async () => { await goToPopup(driver, rootURL); const label = await querySelector( driver, @@ -76,20 +86,132 @@ describe('Watch wallet flow', () => { ); const actual = await label.getText(); - const expected = ['0x70c1...43C4', 'djweth.eth']; + const expected = ['0x70c1...43C4', watchedWallet]; expect(expected.includes(actual)).toEqual(true); }); - it('should be able to lock and unlock the extension', async () => { - // Lock + it('should be able to add a new wallet via pkey', async () => { + await goToPopup(driver, rootURL, '#/home'); + await findElementByIdAndClick({ + id: 'header-account-name-shuffle', + driver, + }); + await findElementByTestIdAndClick({ id: 'add-wallet-button', driver }); await findElementByTestIdAndClick({ - id: 'home-page-header-right', + id: 'import-wallets-button', driver, }); - await findElementByTestIdAndClick({ id: 'lock', driver }); - // Unlock - await typeOnTextInput({ id: 'password-input', driver, text: 'test1234' }); - await findElementByTestIdAndClick({ id: 'unlock-button', driver }); + await typeOnTextInput({ + id: 'secret-textarea', + driver, + text: privateKey, + }); + + await findElementByTestIdAndClick({ + id: 'import-wallets-button', + driver, + }); + await delayTime('medium'); + + it('should display pk account wallet name', async () => { + const account = await getTextFromText({ id: 'account-name', driver }); + expect(account).toBe(await shortenAddress(pkWallet)); + }); + }); + + it('should be able to add a new wallet via watch', async () => { + await goToPopup(driver, rootURL, '#/home'); + await findElementByIdAndClick({ + id: 'header-account-name-shuffle', + driver, + }); + await findElementByTestIdAndClick({ id: 'add-wallet-button', driver }); + await findElementByTestIdAndClick({ + id: 'watch-wallets-button', + driver, + }); + + await typeOnTextInput({ + id: 'secret-textarea', + driver, + text: watchedWalletTwo, + }); + + await findElementByTestIdAndClick({ + id: 'watch-wallets-button', + driver, + }); + await delayTime('medium'); + + it('should display watched account name', async () => { + await goToPopup(driver, rootURL); + const label = await querySelector( + driver, + '[data-testid="header"] [data-testid="account-name"]', + ); + + const actual = await label.getText(); + const expected = ['0x089b...be9E', watchedWalletTwo]; + expect(expected.includes(actual)).toEqual(true); + }); + }); + + it('should be able to add a new wallet via seed', async () => { + await goToPopup(driver, rootURL, '#/home'); + await findElementByIdAndClick({ + id: 'header-account-name-shuffle', + driver, + }); + await findElementByTestIdAndClick({ id: 'add-wallet-button', driver }); + await findElementByTestIdAndClick({ + id: 'import-wallets-button', + driver, + }); + + await typeOnTextInput({ + id: 'secret-textarea', + driver, + text: 'test test test test test test test test test test test junk', + }); + + await findElementByTestIdAndClick({ + id: 'import-wallets-button', + driver, + }); + await findElementByTestIdAndClick({ + id: 'add-wallets-button', + driver, + }); + await delayTime('medium'); + + it('should display seed account name', async () => { + const account = await getTextFromText({ id: 'account-name', driver }); + expect(account).toBe(await shortenAddress(pkWallet)); + }); + }); + + it('should be able to switch to the watched wallet', async () => { + await switchWallet(watchedWallet, rootURL, driver); + const wallet = await getTextFromText({ id: 'account-name', driver }); + expect(wallet).toBe(await shortenAddress(watchedWallet)); + }); + + it('should be able to switch to the pk wallet', async () => { + await switchWallet(pkWallet, rootURL, driver); + const wallet = await getTextFromText({ id: 'account-name', driver }); + expect(wallet).toBe(await shortenAddress(pkWallet)); + }); + + it('should be able to switch to the seed wallet', async () => { + await switchWallet(seedWallet, rootURL, driver); + const wallet = await getTextFromText({ id: 'account-name', driver }); + expect(wallet).toBe(await shortenAddress(seedWallet)); + }); + + it('should be able to switch to the second watched wallet', async () => { + await switchWallet(watchedWalletTwo, rootURL, driver); + const wallet = await getTextFromText({ id: 'account-name', driver }); + expect(wallet).toBe(await shortenAddress(watchedWalletTwo)); }); }); diff --git a/e2e/serial/5_dappInteractionFlow.test.ts b/e2e/serial/5_dappInteractionFlow.test.ts index e8f007893f..b76adc376b 100644 --- a/e2e/serial/5_dappInteractionFlow.test.ts +++ b/e2e/serial/5_dappInteractionFlow.test.ts @@ -9,6 +9,7 @@ import { findElementByTestId, findElementByTestIdAndClick, findElementByText, + findElementByTextAndClick, getAllWindowHandles, getExtensionIdByName, getOnchainBalance, @@ -17,6 +18,7 @@ import { goToPopup, goToWelcome, initDriverWithOptions, + shortenAddress, transactionStatus, typeOnTextInput, waitAndClick, @@ -30,7 +32,7 @@ const os = process.env.OS || 'mac'; const walletAddress = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; const recipientWalletAddress = '0x2f318C334780961FB129D2a6c30D0763d9a5C970'; // eslint-disable-next-line prettier/prettier -const shortenedAddress = `${walletAddress.substring(0, 6)}...${walletAddress.substring(38, 42)}`; +const shortenedAddress = await shortenAddress(walletAddress); describe('App interactions flow', () => { beforeAll(async () => { @@ -253,11 +255,7 @@ describe('App interactions flow', () => { driver, }); - const connection = await findElementByText( - driver, - 'bx-e2e-dapp.vercel.app', - ); - await waitAndClick(connection, driver); + await findElementByTextAndClick(driver, 'bx-e2e-dapp.vercel.app'); await findElementByTestIdAndClick({ id: 'switch-network-item-5', driver }); await driver.get('https://bx-e2e-dapp.vercel.app/'); diff --git a/src/entries/popup/pages/walletSwitcher/addWallet.tsx b/src/entries/popup/pages/walletSwitcher/addWallet.tsx index 27637681ff..1bf4e5cf8f 100644 --- a/src/entries/popup/pages/walletSwitcher/addWallet.tsx +++ b/src/entries/popup/pages/walletSwitcher/addWallet.tsx @@ -51,6 +51,7 @@ const AddWallet = () => { subtitle={i18n.t('add_wallet.create_wallet_description')} symbolColor="pink" symbol="plus.circle" + testId={'create-wallets-button'} /> { subtitle={i18n.t('add_wallet.import_wallet_description')} symbolColor="purple" symbol="lock.rotation" + testId={'import-wallets-button'} /> { subtitle={i18n.t('add_wallet.hardware_wallet_description')} symbolColor="blue" symbol="doc.text.magnifyingglass" + testId={'hardware-wallets-button'} /> { subtitle={i18n.t('add_wallet.watch_address_description')} symbolColor="green" symbol="magnifyingglass.circle" + testId={'watch-wallets-button'} /> diff --git a/src/entries/popup/pages/walletSwitcher/index.tsx b/src/entries/popup/pages/walletSwitcher/index.tsx index 7638885991..2666d563ce 100644 --- a/src/entries/popup/pages/walletSwitcher/index.tsx +++ b/src/entries/popup/pages/walletSwitcher/index.tsx @@ -426,6 +426,7 @@ export function WalletSwitcher() { height="32px" width="full" borderRadius="9px" + testId={'add-wallet-button'} > {i18n.t('wallet_switcher.add_another_wallet')} diff --git a/src/entries/popup/pages/wallets/index.tsx b/src/entries/popup/pages/wallets/index.tsx index b0d03e9da8..308f0cae66 100644 --- a/src/entries/popup/pages/wallets/index.tsx +++ b/src/entries/popup/pages/wallets/index.tsx @@ -575,6 +575,7 @@ export function Wallets() { gap="24px" padding="20px" style={{ overflow: 'auto' }} + testId={'wallet-address-or-ens'} > {isUnlocked ? (