From 6aac1593c5c3ffaa34440a226d29b01fae43e89d Mon Sep 17 00:00:00 2001 From: Devin <168687171+Devin-Apps@users.noreply.github.com> Date: Tue, 2 Jul 2024 04:07:06 +0530 Subject: [PATCH 01/10] chore: Create a story for RestoreVaultPage component (#25284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR adds a Storybook story for the `RestoreVaultPage` component. The story allows for isolated testing and visualization of the `RestoreVaultPage` component within the Storybook UI. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25284?quickstart=1) ## **Related issues** ## **Manual testing steps** 1. Go to the latest build of storybook in this PR 2. Navigate to the `RestoreVaultPage` component in the `Pages/Keychains` folder. ## **Screenshots/Recordings** Screenshot 2024-06-13 at 7 34 21 AM Screenshot 2024-06-13 at 7 34 48 AM ## **Pre-merge author checklist** - [X] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. [Devin Run](https://preview.devin.ai/devin/6d166713059149e5816c5666c2c85ed9) Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ui/pages/keychains/restore-vault.stories.tsx | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 ui/pages/keychains/restore-vault.stories.tsx diff --git a/ui/pages/keychains/restore-vault.stories.tsx b/ui/pages/keychains/restore-vault.stories.tsx new file mode 100644 index 000000000000..ae87ad4ac70e --- /dev/null +++ b/ui/pages/keychains/restore-vault.stories.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import RestoreVaultPage from './restore-vault'; + +const mockStore = configureStore([]); +const store = mockStore({ + appState: { + isLoading: false, + }, +}); + +const meta: Meta = { + title: 'Pages/Keychains/RestoreVaultPage', + component: RestoreVaultPage, + decorators: [(Story) => ], + argTypes: { + createNewVaultAndRestore: { action: 'createNewVaultAndRestore' }, + leaveImportSeedScreenState: { action: 'leaveImportSeedScreenState' }, + history: { control: 'object' }, + isLoading: { control: 'boolean' }, + }, + args: { + createNewVaultAndRestore: () => {}, + leaveImportSeedScreenState: () => {}, + history: { push: () => {} }, + isLoading: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const DefaultStory: Story = {}; + +DefaultStory.storyName = 'Default'; From d9ce2dd04b76e39a1c87f8885e2877bab955d8d4 Mon Sep 17 00:00:00 2001 From: Devin <168687171+Devin-Apps@users.noreply.github.com> Date: Tue, 2 Jul 2024 04:08:44 +0530 Subject: [PATCH 02/10] refactor: Replace deprecated mixins with Text component in unlock-page.component.js (#25227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This pull request replaces the deprecated mixins in the `unlock-page.component.js` and `index.scss` files with the `Text` component. The changes include updating the `unlock-page__title` class to use the `Text` component with appropriate properties and removing the deprecated mixin instance from the SCSS file. Devin Run Link: https://preview.devin.ai/devin/de079f9a40fd45adb09783a36409256c [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25227?quickstart=1) ## **Related issues** Partially Fixes: https://github.com/MetaMask/metamask-extension/issues/20496 ## **Manual testing steps** 1. Go to the latest build of storybook in this PR 2. Verify that the "UnlockPage" component renders correctly with the updated `Text` component 3. Ensure that the `unlock-page__title` class is replaced with `[data-testid="unlock-page-title"]` in the `test/e2e/tests/settings/auto-lock.spec.js` file ## **Screenshots/Recordings** ### **Before** ![](https://api.devin.ai/attachments/991317c8-2109-4a41-bf0c-5d3edd8add50/980098aa-0372-4728-9f89-f8eff532a8f2.png) ### **After** ![](https://api.devin.ai/attachments/49521e22-bbd6-4016-81a2-839816a3008f/0361fee1-e517-4e0e-8959-f22c6b2e1fac.png) ## **Pre-merge author checklist** - [X] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: George Marshall --- test/e2e/tests/settings/auto-lock.spec.js | 4 +++- .../__snapshots__/unlock-page.test.js.snap | 3 ++- ui/pages/unlock-page/index.scss | 8 -------- ui/pages/unlock-page/unlock-page.component.js | 12 +++++++++++- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/test/e2e/tests/settings/auto-lock.spec.js b/test/e2e/tests/settings/auto-lock.spec.js index 400f2d45aab8..bded29fa5f39 100644 --- a/test/e2e/tests/settings/auto-lock.spec.js +++ b/test/e2e/tests/settings/auto-lock.spec.js @@ -42,7 +42,9 @@ describe('Auto-Lock Timer', function () { '[data-testid="advanced-setting-auto-lock"] button', ); // Verify the wallet is locked - const pageTitle = await driver.findElement('.unlock-page__title'); + const pageTitle = await driver.findElement( + '[data-testid="unlock-page-title"]', + ); const unlockButton = await driver.findElement('.unlock-page button'); assert.equal(await pageTitle.getText(), 'Welcome back!'); assert.equal(await unlockButton.isDisplayed(), true); diff --git a/ui/pages/unlock-page/__snapshots__/unlock-page.test.js.snap b/ui/pages/unlock-page/__snapshots__/unlock-page.test.js.snap index beb7e82699be..0a572596aa2c 100644 --- a/ui/pages/unlock-page/__snapshots__/unlock-page.test.js.snap +++ b/ui/pages/unlock-page/__snapshots__/unlock-page.test.js.snap @@ -19,7 +19,8 @@ exports[`Unlock Page should match snapshot 1`] = `

Welcome back!

diff --git a/ui/pages/unlock-page/index.scss b/ui/pages/unlock-page/index.scss index abb1f837bdef..7d98b84f96a9 100644 --- a/ui/pages/unlock-page/index.scss +++ b/ui/pages/unlock-page/index.scss @@ -35,14 +35,6 @@ } } - &__title { - @include design-system.H2; - - margin-top: 5px; - font-weight: 800; - color: var(--color-text-alternative); - } - &__form { width: 100%; margin: 56px 0 8px; diff --git a/ui/pages/unlock-page/unlock-page.component.js b/ui/pages/unlock-page/unlock-page.component.js index e97f0b2f3291..504873b1c1cc 100644 --- a/ui/pages/unlock-page/unlock-page.component.js +++ b/ui/pages/unlock-page/unlock-page.component.js @@ -1,6 +1,8 @@ import { EventEmitter } from 'events'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import { Text } from '../../components/component-library'; +import { TextVariant, TextColor } from '../../helpers/constants/design-system'; import Button from '../../components/ui/button'; import TextField from '../../components/ui/text-field'; import Mascot from '../../components/ui/mascot'; @@ -176,7 +178,15 @@ export default class UnlockPage extends Component { ) : null} -

{t('welcomeBack')}

+ + {t('welcomeBack')} +
{t('unlockMessage')}
Date: Tue, 2 Jul 2024 04:30:30 +0530 Subject: [PATCH 03/10] refactor: Replace deprecated mixins with Text component in selected-account.component.js (#25262) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Replaced deprecated mixins with Text component in `selected-account.component.js`. The change aims to modernize the codebase by using the Text component from the design system, ensuring consistency and maintainability. Devin Run Link: https://preview.devin.ai/devin/6dcddd7b3ee2456ca004b34d033b0d82 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25262?quickstart=1) ## **Related issues** Partially Fixes: https://github.com/MetaMask/metamask-extension/issues/20496 ## **Manual testing steps** 1. Go to the latest build of storybook in this PR 2. Verify the "SelectedAccount" component displays correctly with the updated Text component. ## **Screenshots/Recordings** ### **Before** ![](https://api.devin.ai/attachments/ad6f5c71-8714-4f0d-9675-d36481abbafa/before_changes_selected-account.component.png) ### **After** Screenshot 2024-06-12 at 21 55 36 ## **Pre-merge author checklist** - [X] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [X] I've completed the PR template to the best of my ability - [X] I’ve included tests if applicable - [X] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [X] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: George Marshall Co-authored-by: Shreyasi Mandal --- ui/components/app/selected-account/index.scss | 21 ----------- .../selected-account.component.js | 35 +++++++++++++++---- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/ui/components/app/selected-account/index.scss b/ui/components/app/selected-account/index.scss index 5c2be851e62c..bf4b3887c4f8 100644 --- a/ui/components/app/selected-account/index.scss +++ b/ui/components/app/selected-account/index.scss @@ -11,27 +11,6 @@ width: 100%; } - &__name { - @include design-system.H5; - - width: 100%; - font-weight: 500; - color: var(--color-text-default); - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - text-align: center; - margin-bottom: 4px; - } - - &__address { - @include design-system.H7; - - color: var(--color-text-alternative); - display: flex; - align-items: center; - } - &__clickable { display: flex; flex-direction: column; diff --git a/ui/components/app/selected-account/selected-account.component.js b/ui/components/app/selected-account/selected-account.component.js index 0ab706deab69..ee13d7f786d3 100644 --- a/ui/components/app/selected-account/selected-account.component.js +++ b/ui/components/app/selected-account/selected-account.component.js @@ -11,8 +11,17 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import CustodyLabels from '../../institutional/custody-labels/custody-labels'; ///: END:ONLY_INCLUDE_IF -import { Icon, IconName, IconSize } from '../../component-library'; -import { IconColor } from '../../../helpers/constants/design-system'; +import { Icon, IconName, IconSize, Text } from '../../component-library'; +import { + IconColor, + TextVariant, + TextColor, + TextAlign, + BlockSize, + Display, + FontWeight, + AlignItems, +} from '../../../helpers/constants/design-system'; import { COPY_OPTIONS } from '../../../../shared/constants/copy'; class SelectedAccount extends Component { @@ -108,10 +117,24 @@ class SelectedAccount extends Component { copyToClipboard(checksummedAddress, COPY_OPTIONS); }} > -
+ {selectedAccount.metadata.name} -
-
+ + { ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) showCustodyLabels && @@ -132,7 +155,7 @@ class SelectedAccount extends Component { />
)} - + From b893161ad7afe76f5cefa1d37cc8009581836ec1 Mon Sep 17 00:00:00 2001 From: chloeYue <105063779+chloeYue@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:33:15 +0200 Subject: [PATCH 04/10] test: Initial PR for integrating the Page Object Model (POM) into e2e test suite (#25373) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** The Page Object Model (POM) is a design pattern that promotes code reusability and maintainability in e2e testing by encapsulating UI element selectors and interactions within page objects. This initial PR is the beginning of integrating POM into e2e test suite, aiming to enhance test code quality. The entire implementation is done in TypeScript instead of JavaScript. Key Considerations for Implementation: - Easy Adaptation: ensure that the structure is straightforward and intuitive, thereby accelerating the development speed and facilitating easier migration. - Enhanced Logging: offer better insights during test execution and debugging, making it easier to investigate flaky test Structure: The POM structure is organized around distinct page objects for each application UI screen. (Common components, such as the HeaderNavbar, are directly integrated as part of a screen's component when interaction is required. This approach eliminates the need for explicit class extension and allows for the exclusion of the HeaderNavbar in screens where interaction with it is unnecessary.) Page functions and process: I've introduced page objects, each designed to encapsulate the elements and interactions specific to their respective pages. Additionally, I've implemented processes such as `loginWaitBalance` and `sendTransaction` to efficiently manage common test flows that require interactions across multiple screens. Processes should be defined for sequences that involve multiple page objects, facilitating broader testing objectives like completing transactions or logging in. These are typically actions that navigate through several screens. Functions within a class (page object) are best used for actions and verifications that are specific to a single page. This approach promotes the encapsulation and reusability of code for individual UI components or screens, making the tests more modular and maintainable. 3 Migrated Test Cases: Migrated 3 transaction test cases to the new POM structure, showcasing the improved test architecture and log information. These migrations serve as a POC, demonstrating the effectiveness of POM in our testing environment and provide the base for future migrations. Additionally, I'm eliminating CSS selectors that aren't robust and replacing them with more stable selectors in this PR. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25373?quickstart=1) ## **Related issues** Fixes: #24985 Relates to: #22464 ## **Manual testing steps** Tests should pass on CI. Code should be easy to understand. ## **Screenshots/Recordings** ### **Before** ![Screenshot 2024-06-14 at 12 01 32](https://github.com/MetaMask/metamask-extension/assets/105063779/e0a48e9e-d8e1-4508-8b3b-6e1923b65efc) ![Screenshot 2024-06-18 at 11 25 05](https://github.com/MetaMask/metamask-extension/assets/105063779/d10f9bc8-9a3c-4d80-a341-0d7a8fcf73fc) ### **After** ![Screenshot 2024-06-14 at 22 38 22](https://github.com/MetaMask/metamask-extension/assets/105063779/35b1b150-dc0c-436b-9062-af0a71dd48ef) ![Screenshot 2024-06-18 at 11 28 10](https://github.com/MetaMask/metamask-extension/assets/105063779/ddbb953c-7839-4d6c-ae1a-07fa1e825f7d) ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- test/e2e/constants.ts | 3 + test/e2e/helpers.js | 18 +- test/e2e/page-objects/pages/header-navbar.ts | 18 ++ test/e2e/page-objects/pages/homepage.ts | 165 ++++++++++++++++++ test/e2e/page-objects/pages/login-page.ts | 45 +++++ .../pages/send/confirm-tx-page.ts | 58 ++++++ .../pages/send/send-token-page.ts | 131 ++++++++++++++ .../page-objects/processes/login.process.ts | 30 ++++ .../processes/send-transaction.process.ts | 43 +++++ test/e2e/tests/transaction/ens.spec.js | 124 ------------- test/e2e/tests/transaction/ens.spec.ts | 108 ++++++++++++ .../e2e/tests/transaction/simple-send.spec.js | 28 --- .../e2e/tests/transaction/simple-send.spec.ts | 32 ++++ .../stuck-approved-transaction.spec.js | 40 ----- .../stuck-approved-transaction.spec.ts | 28 +++ test/e2e/webdriver/README.md | 27 +++ test/e2e/webdriver/driver.js | 21 +++ .../domain-input-resolution-cell.tsx | 10 +- .../confirm-transaction-base.component.js | 5 +- .../add-recipient/domain-input.component.js | 5 +- 20 files changed, 738 insertions(+), 201 deletions(-) create mode 100644 test/e2e/page-objects/pages/header-navbar.ts create mode 100644 test/e2e/page-objects/pages/homepage.ts create mode 100644 test/e2e/page-objects/pages/login-page.ts create mode 100644 test/e2e/page-objects/pages/send/confirm-tx-page.ts create mode 100644 test/e2e/page-objects/pages/send/send-token-page.ts create mode 100644 test/e2e/page-objects/processes/login.process.ts create mode 100644 test/e2e/page-objects/processes/send-transaction.process.ts delete mode 100644 test/e2e/tests/transaction/ens.spec.js create mode 100644 test/e2e/tests/transaction/ens.spec.ts delete mode 100644 test/e2e/tests/transaction/simple-send.spec.js create mode 100644 test/e2e/tests/transaction/simple-send.spec.ts delete mode 100644 test/e2e/tests/transaction/stuck-approved-transaction.spec.js create mode 100644 test/e2e/tests/transaction/stuck-approved-transaction.spec.ts diff --git a/test/e2e/constants.ts b/test/e2e/constants.ts index ad1689e570d6..86dda727a040 100644 --- a/test/e2e/constants.ts +++ b/test/e2e/constants.ts @@ -35,3 +35,6 @@ export const TEST_SNAPS_SIMPLE_KEYRING_WEBSITE_URL = /* Address of the VerifyingPaymaster smart contract deployed to Ganache. */ export const VERIFYING_PAYMASTER = '0xbdbDEc38ed168331b1F7004cc9e5392A2272C1D7'; + +/* Default ganache ETH balance in decimal when first login */ +export const DEFAULT_GANACHE_ETH_BALANCE_DEC = '25'; diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index ba32c94cabec..226fe4f9f6ac 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -16,7 +16,10 @@ const { PAGES } = require('./webdriver/driver'); const GanacheSeeder = require('./seeder/ganache-seeder'); const { Bundler } = require('./bundler'); const { SMART_CONTRACTS } = require('./seeder/smart-contracts'); -const { ERC_4337_ACCOUNT } = require('./constants'); +const { + ERC_4337_ACCOUNT, + DEFAULT_GANACHE_ETH_BALANCE_DEC, +} = require('./constants'); const tinyDelayMs = 200; const regularDelayMs = tinyDelayMs * 2; @@ -730,25 +733,30 @@ const ACCOUNT_1 = '0x5cfe73b6021e818b776b421b1c4db2474086a7e1'; const ACCOUNT_2 = '0x09781764c08de8ca82e156bbf156a3ca217c7950'; const defaultGanacheOptions = { - accounts: [{ secretKey: PRIVATE_KEY, balance: convertETHToHexGwei(25) }], + accounts: [ + { + secretKey: PRIVATE_KEY, + balance: convertETHToHexGwei(DEFAULT_GANACHE_ETH_BALANCE_DEC), + }, + ], }; const multipleGanacheOptions = { accounts: [ { secretKey: PRIVATE_KEY, - balance: convertETHToHexGwei(25), + balance: convertETHToHexGwei(DEFAULT_GANACHE_ETH_BALANCE_DEC), }, { secretKey: PRIVATE_KEY_TWO, - balance: convertETHToHexGwei(25), + balance: convertETHToHexGwei(DEFAULT_GANACHE_ETH_BALANCE_DEC), }, ], }; const generateGanacheOptions = ({ secretKey = PRIVATE_KEY, - balance = convertETHToHexGwei(25), + balance = convertETHToHexGwei(DEFAULT_GANACHE_ETH_BALANCE_DEC), ...otherProps }) => { const accounts = [ diff --git a/test/e2e/page-objects/pages/header-navbar.ts b/test/e2e/page-objects/pages/header-navbar.ts new file mode 100644 index 000000000000..add85e88b7b7 --- /dev/null +++ b/test/e2e/page-objects/pages/header-navbar.ts @@ -0,0 +1,18 @@ +import { Driver } from '../../webdriver/driver'; + +class HeaderNavbar { + private driver: Driver; + + private accountMenuButton: string; + + constructor(driver: Driver) { + this.driver = driver; + this.accountMenuButton = '[data-testid="account-menu-icon"]'; + } + + async openAccountMenu(): Promise { + await this.driver.clickElement(this.accountMenuButton); + } +} + +export default HeaderNavbar; diff --git a/test/e2e/page-objects/pages/homepage.ts b/test/e2e/page-objects/pages/homepage.ts new file mode 100644 index 000000000000..5e596021a449 --- /dev/null +++ b/test/e2e/page-objects/pages/homepage.ts @@ -0,0 +1,165 @@ +import { strict as assert } from 'assert'; +import { Driver } from '../../webdriver/driver'; +import { DEFAULT_GANACHE_ETH_BALANCE_DEC } from '../../constants'; +import HeaderNavbar from './header-navbar'; + +class HomePage { + private driver: Driver; + + private sendButton: string; + + private activityTab: string; + + private tokensTab: string; + + private balance: string; + + private completedTransactions: string; + + private confirmedTransactions: object; + + private transactionAmountsInActivity: string; + + public headerNavbar: HeaderNavbar; + + constructor(driver: Driver) { + this.driver = driver; + this.headerNavbar = new HeaderNavbar(driver); + this.sendButton = '[data-testid="eth-overview-send"]'; + this.activityTab = '[data-testid="account-overview__activity-tab"]'; + this.tokensTab = '[data-testid="account-overview__asset-tab"]'; + this.confirmedTransactions = { + text: 'Confirmed', + css: '.transaction-status-label--confirmed', + }; + this.balance = '[data-testid="eth-overview__primary-currency"]'; + this.completedTransactions = '[data-testid="activity-list-item"]'; + this.transactionAmountsInActivity = + '[data-testid="transaction-list-item-primary-currency"]'; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.sendButton, + this.activityTab, + this.tokensTab, + ]); + } catch (e) { + console.log('Timeout while waiting for home page to be loaded', e); + throw e; + } + console.log('Home page is loaded'); + } + + async check_expectedBalanceIsDisplayed( + expectedBalance: string = DEFAULT_GANACHE_ETH_BALANCE_DEC, + ): Promise { + try { + await this.driver.waitForSelector({ + css: this.balance, + text: `${expectedBalance} ETH`, + }); + } catch (e) { + const balance = await this.driver.waitForSelector(this.balance); + const currentBalance = parseFloat(await balance.getText()); + const errorMessage = `Expected balance ${expectedBalance} ETH, got balance ${currentBalance} ETH`; + console.log(errorMessage, e); + throw e; + } + console.log( + `Expected balance ${expectedBalance} ETH is displayed on homepage`, + ); + } + + async startSendFlow(): Promise { + await this.driver.clickElement(this.sendButton); + } + + async goToActivityList(): Promise { + console.log(`Open activity tab on homepage`); + await this.driver.clickElement(this.activityTab); + } + + /** + * This function checks if the specified number of confirmed transactions are displayed in the activity list on homepage. + * It waits up to 10 seconds for the expected number of confirmed transactions to be visible. + * + * @param expectedNumber - The number of confirmed transactions expected to be displayed in activity list. Defaults to 1. + * @returns A promise that resolves if the expected number of confirmed transactions is displayed within the timeout period. + */ + async check_confirmedTxNumberDisplayedInActivity( + expectedNumber: number = 1, + ): Promise { + console.log( + `Wait for ${expectedNumber} confirmed transactions to be displayed in activity list`, + ); + await this.driver.wait(async () => { + const confirmedTxs = await this.driver.findElements( + this.confirmedTransactions, + ); + return confirmedTxs.length === expectedNumber; + }, 10000); + console.log( + `${expectedNumber} confirmed transactions found in activity list on homepage`, + ); + } + + /** + * This function checks the specified number of completed transactions are displayed in the activity list on the homepage. + * It waits up to 10 seconds for the expected number of completed transactions to be visible. + * + * @param expectedNumber - The number of completed transactions expected to be displayed in the activity list. Defaults to 1. + * @returns A promise that resolves if the expected number of completed transactions is displayed within the timeout period. + */ + async check_completedTxNumberDisplayedInActivity( + expectedNumber: number = 1, + ): Promise { + console.log( + `Wait for ${expectedNumber} completed transactions to be displayed in activity list`, + ); + await this.driver.wait(async () => { + const completedTxs = await this.driver.findElements( + this.completedTransactions, + ); + return completedTxs.length === expectedNumber; + }, 10000); + console.log( + `${expectedNumber} completed transactions found in activity list on homepage`, + ); + } + + /** + * This function checks if a specified transaction amount at the specified index matches the expected one. + * + * @param expectedAmount - The expected transaction amount to be displayed. Defaults to '-1 ETH'. + * @param expectedNumber - The 1-based index of the transaction in the activity list whose amount is to be checked. + * Defaults to 1, indicating the first transaction in the list. + * @returns A promise that resolves if the transaction amount at the specified index matches the expected amount. + * The promise is rejected if the amounts do not match or if an error occurs during the process. + * @example + * // To check if the third transaction in the activity list displays an amount of '2 ETH' + * await check_txAmountInActivity('2 ETH', 3); + */ + async check_txAmountInActivity( + expectedAmount: string = '-1 ETH', + expectedNumber: number = 1, + ): Promise { + const transactionAmounts = await this.driver.findElements( + this.transactionAmountsInActivity, + ); + const transactionAmountsText = await transactionAmounts[ + expectedNumber - 1 + ].getText(); + assert.equal( + transactionAmountsText, + expectedAmount, + `${transactionAmountsText} is displayed as transaction amount instead of ${expectedAmount} for transaction ${expectedNumber}`, + ); + console.log( + `Amount for transaction ${expectedNumber} is displayed as ${expectedAmount}`, + ); + } +} + +export default HomePage; diff --git a/test/e2e/page-objects/pages/login-page.ts b/test/e2e/page-objects/pages/login-page.ts new file mode 100644 index 000000000000..2a3145ccdab5 --- /dev/null +++ b/test/e2e/page-objects/pages/login-page.ts @@ -0,0 +1,45 @@ +import { Driver } from '../../webdriver/driver'; + +class LoginPage { + private driver: Driver; + + private passwordInput: string; + + private unlockButton: string; + + private welcomeBackMessage: object; + + constructor(driver: Driver) { + this.driver = driver; + this.passwordInput = '[data-testid="unlock-password"]'; + this.unlockButton = '[data-testid="unlock-submit"]'; + this.welcomeBackMessage = { + css: '.unlock-page__title', + text: 'Welcome back!', + }; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.welcomeBackMessage, + this.passwordInput, + this.unlockButton, + ]); + } catch (e) { + console.log('Timeout while waiting for login page to be loaded', e); + throw e; + } + console.log('Login page is loaded'); + } + + async fillPassword(password: string): Promise { + await this.driver.fill(this.passwordInput, password); + } + + async clickUnlockButton(): Promise { + await this.driver.clickElement(this.unlockButton); + } +} + +export default LoginPage; diff --git a/test/e2e/page-objects/pages/send/confirm-tx-page.ts b/test/e2e/page-objects/pages/send/confirm-tx-page.ts new file mode 100644 index 000000000000..0b51f9c6c4aa --- /dev/null +++ b/test/e2e/page-objects/pages/send/confirm-tx-page.ts @@ -0,0 +1,58 @@ +import { Driver } from '../../../webdriver/driver'; + +class ConfirmTxPage { + private driver: Driver; + + private confirmButton: string; + + private totalFee: string; + + private transactionFee: string; + + constructor(driver: Driver) { + this.driver = driver; + this.confirmButton = '[data-testid="page-container-footer-next"]'; + this.transactionFee = '[data-testid="confirm-gas-display"]'; + this.totalFee = '[data-testid="confirm-page-total-amount"]'; + } + + /** + * Verifies that the confirm transaction page is fully loaded by checking for the presence of confirm button and the expected gas values. + * + * @param expectedGasFee - The expected gas fee value to be displayed on the page. + * @param expectedTotalFee - The expected total fee value to be displayed on the page. + * @returns A promise that resolves when all specified elements are verified to be present and contain the expected values, indicating the page has fully loaded. + */ + async check_pageIsLoaded( + expectedGasFee: string, + expectedTotalFee: string, + ): Promise { + try { + await Promise.all([ + this.driver.waitForSelector(this.confirmButton), + this.driver.waitForSelector({ + css: this.transactionFee, + text: `${expectedGasFee} ETH`, + }), + this.driver.waitForSelector({ + css: this.totalFee, + text: `${expectedTotalFee} ETH`, + }), + ]); + } catch (e) { + console.log( + `Timeout while waiting for confirm transaction screen to be loaded, expected gas fee is: ${expectedGasFee} and expected total fee is: ${expectedTotalFee}`, + e, + ); + throw e; + } + console.log('Confirm transaction page is loaded with expected gas value'); + } + + async confirmTx(): Promise { + console.log('Click confirm button to confirm transaction'); + await this.driver.clickElement(this.confirmButton); + } +} + +export default ConfirmTxPage; diff --git a/test/e2e/page-objects/pages/send/send-token-page.ts b/test/e2e/page-objects/pages/send/send-token-page.ts new file mode 100644 index 000000000000..b5b703814a5f --- /dev/null +++ b/test/e2e/page-objects/pages/send/send-token-page.ts @@ -0,0 +1,131 @@ +import { strict as assert } from 'assert'; +import { Driver } from '../../../webdriver/driver'; + +class SendTokenPage { + private driver: Driver; + + private inputRecipient: string; + + private inputAmount: string; + + private scanButton: string; + + private continueButton: object; + + private ensResolvedName: string; + + private ensAddressAsRecipient: string; + + private ensResolvedAddress: string; + + constructor(driver: Driver) { + this.driver = driver; + this.inputAmount = '[data-testid="currency-input"]'; + this.inputRecipient = '[data-testid="ens-input"]'; + this.scanButton = '[data-testid="ens-qr-scan-button"]'; + this.ensResolvedName = + '[data-testid="multichain-send-page__recipient__item__title"]'; + this.ensResolvedAddress = + '[data-testid="multichain-send-page__recipient__item__subtitle"]'; + this.ensAddressAsRecipient = '[data-testid="ens-input-selected"]'; + this.continueButton = { + text: 'Continue', + tag: 'button', + }; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.scanButton, + this.inputRecipient, + ]); + } catch (e) { + console.log( + 'Timeout while waiting for send token screen to be loaded', + e, + ); + throw e; + } + console.log('Send token screen is loaded'); + } + + async fillRecipient(recipientAddress: string): Promise { + console.log( + `Fill recipient input with ${recipientAddress} on send token screen`, + ); + await this.driver.pasteIntoField(this.inputRecipient, recipientAddress); + } + + async fillAmount(amount: string): Promise { + console.log(`Fill amount input with ${amount} on send token screen`); + const inputAmount = await this.driver.waitForSelector(this.inputAmount); + await this.driver.pasteIntoField(this.inputAmount, amount); + // The return value is not ts-compatible, requiring a temporary any cast to access the element's value. This will be corrected with the driver function's ts migration. + // TODO: Replace `any` with type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inputValue = await (inputAmount as any).getProperty('value'); + assert.equal( + inputValue, + amount, + `Error when filling amount field on send token screen: the value entered is ${inputValue} instead of expected ${amount}.`, + ); + } + + async goToNextScreen(): Promise { + await this.driver.clickElement(this.continueButton); + } + + /** + * Verifies that an ENS domain correctly resolves to the specified Ethereum address on the send token screen. + * + * @param ensDomain - The ENS domain name expected to resolve (e.g., "test.eth"). + * @param address - The Ethereum address to which the ENS domain is expected to resolve. + * @returns A promise that resolves if the ENS domain successfully resolves to the specified address on send token screen. + */ + async check_ensAddressResolution( + ensDomain: string, + address: string, + ): Promise { + console.log( + `Check ENS domain resolution: '${ensDomain}' should resolve to address '${address}' on the send token screen.`, + ); + // check if ens domain is resolved as expected address + await this.driver.waitForSelector({ + text: ensDomain, + css: this.ensResolvedName, + }); + await this.driver.waitForSelector({ + text: address, + css: this.ensResolvedAddress, + }); + } + + /** + * Verifies that an address resolved via ENS can be selected as the recipient on the send token screen. + * + * @param ensDomain - The ENS domain name expected to resolve to the given address. + * @param address - The Ethereum address to which the ENS domain is expected to resolve. + * @returns A promise that resolves if the ENS domain can be successfully used as a recipient address on the send token screen. + */ + async check_ensAddressAsRecipient( + ensDomain: string, + address: string, + ): Promise { + // click to select the resolved adress + await this.driver.clickElement({ + text: ensDomain, + css: this.ensResolvedName, + }); + // user should be able to send token to the resolved address + await this.driver.waitForSelector({ + css: this.ensAddressAsRecipient, + text: ensDomain + address, + }); + console.log( + `ENS domain '${ensDomain}' resolved to address '${address}' and can be used as recipient on send token screen.`, + ); + } +} + +export default SendTokenPage; diff --git a/test/e2e/page-objects/processes/login.process.ts b/test/e2e/page-objects/processes/login.process.ts new file mode 100644 index 000000000000..93bc1d3c18d3 --- /dev/null +++ b/test/e2e/page-objects/processes/login.process.ts @@ -0,0 +1,30 @@ +import LoginPage from '../pages/login-page'; +import HomePage from '../pages/homepage'; +import { Driver } from '../../webdriver/driver'; +import { DEFAULT_GANACHE_ETH_BALANCE_DEC } from '../../constants'; +import { WALLET_PASSWORD } from '../../helpers'; + +/** + * This method unlocks the wallet and verifies that the user lands on the homepage with the expected balance. It is designed to be the initial step in setting up a test environment. + * + * @param driver - The webdriver instance. + * @param password - The password used to unlock the wallet. Defaults to WALLET_PASSWORD. + * @param expectedBalance - The expected balance to be displayed on the homepage after successful login. Defaults to DEFAULT_GANACHE_ETH_BALANCE_DEC, reflecting common usage in test setups. + */ +export const loginWithBalanceValidaiton = async ( + driver: Driver, + password: string = WALLET_PASSWORD, + expectedBalance: string = DEFAULT_GANACHE_ETH_BALANCE_DEC, +) => { + console.log('Navigate to unlock page and try to login with pasword'); + await driver.navigate(); + const loginPage = new LoginPage(driver); + await loginPage.check_pageIsLoaded(); + await loginPage.fillPassword(password); + await loginPage.clickUnlockButton(); + + // user should land on homepage after successfully logging in with password + const homePage = new HomePage(driver); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(expectedBalance); +}; diff --git a/test/e2e/page-objects/processes/send-transaction.process.ts b/test/e2e/page-objects/processes/send-transaction.process.ts new file mode 100644 index 000000000000..2ff94f49aced --- /dev/null +++ b/test/e2e/page-objects/processes/send-transaction.process.ts @@ -0,0 +1,43 @@ +import HomePage from '../pages/homepage'; +import ConfirmTxPage from '../pages/send/confirm-tx-page'; +import SendTokenPage from '../pages/send/send-token-page'; +import { Driver } from '../../webdriver/driver'; + +/** + * This function initiates the steps required to send a transaction from the homepage to final confirmation. + * + * @param driver - The webdriver instance. + * @param recipientAddress - The recipient address. + * @param amount - The amount of the asset to be sent in the transaction. + * @param gasfee - The expected transaction gas fee. + * @param totalfee - The expected total transaction fee. + */ +export const sendTransaction = async ( + driver: Driver, + recipientAddress: string, + amount: string, + gasfee: string, + totalfee: string, +): Promise => { + console.log( + `Start process to send amount ${amount} to recipient ${recipientAddress} on home screen`, + ); + // click send button on homepage to start process + const homePage = new HomePage(driver); + await homePage.startSendFlow(); + + // user should land on send token screen to fill recipient and amount + const sendToPage = new SendTokenPage(driver); + await sendToPage.check_pageIsLoaded(); + await sendToPage.fillRecipient(recipientAddress); + await sendToPage.fillAmount(amount); + await sendToPage.goToNextScreen(); + + // confirm transaction when user lands on confirm transaction screen + const confirmTxPage = new ConfirmTxPage(driver); + await confirmTxPage.check_pageIsLoaded(gasfee, totalfee); + await confirmTxPage.confirmTx(); + + // user should land on homepage after transaction is confirmed + await homePage.check_pageIsLoaded(); +}; diff --git a/test/e2e/tests/transaction/ens.spec.js b/test/e2e/tests/transaction/ens.spec.js deleted file mode 100644 index db399fef5a93..000000000000 --- a/test/e2e/tests/transaction/ens.spec.js +++ /dev/null @@ -1,124 +0,0 @@ -const { - defaultGanacheOptions, - withFixtures, - openActionMenuAndStartSendFlow, - unlockWallet, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); - -describe('ENS', function () { - const sampleAddress = '1111111111111111111111111111111111111111'; - - // Having 2 versions of the address is a bug(#25286) - const shortSampleAddress = '0x1111...1111'; - const shortSampleAddresV2 = '0x11111...11111'; - - const sampleEnsDomain = 'test.eth'; - const infuraUrl = - 'https://mainnet.infura.io/v3/00000000000000000000000000000000'; - - async function mockInfura(mockServer) { - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_blockNumber' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: '0x1', - }, - }; - }); - - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_getBalance' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: '0x1', - }, - }; - }); - - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_getBlockByNumber' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: {}, - }, - }; - }); - - await mockServer - .forPost(infuraUrl) - .withJsonBodyIncluding({ method: 'eth_call' }) - .thenCallback(() => { - return { - statusCode: 200, - json: { - jsonrpc: '2.0', - id: '1111111111111111', - result: `0x000000000000000000000000${sampleAddress}`, - }, - }; - }); - } - - it('domain resolves to a correct address', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().withNetworkControllerOnMainnet().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - testSpecificMock: mockInfura, - }, - async ({ driver }) => { - await unlockWallet(driver); - - await driver.assertElementNotPresent('.loading-overlay'); - - await openActionMenuAndStartSendFlow(driver); - - await driver.pasteIntoField( - '.ens-input__wrapper__input', - sampleEnsDomain, - ); - - await driver.waitForSelector({ - text: sampleEnsDomain, - css: '[data-testid="multichain-send-page__recipient__item__title"]', - }); - - await driver.waitForSelector({ - text: shortSampleAddress, - css: '.multichain-send-page__recipient__item__subtitle', - }); - - await driver.clickElement({ - text: sampleEnsDomain, - css: '[data-testid="multichain-send-page__recipient__item__title"]', - }); - - await driver.findElement({ - css: '.ens-input__selected-input__title', - text: 'test.eth', - }); - - await driver.findElement({ - text: shortSampleAddresV2, - }); - }, - ); - }); -}); diff --git a/test/e2e/tests/transaction/ens.spec.ts b/test/e2e/tests/transaction/ens.spec.ts new file mode 100644 index 000000000000..c669f1818004 --- /dev/null +++ b/test/e2e/tests/transaction/ens.spec.ts @@ -0,0 +1,108 @@ +import { Suite } from 'mocha'; +import { MockttpServer } from 'mockttp'; +import { + defaultGanacheOptions, + withFixtures, + WALLET_PASSWORD, +} from '../../helpers'; +import { Driver } from '../../webdriver/driver'; +import FixtureBuilder from '../../fixture-builder'; +import { loginWithBalanceValidaiton } from '../../page-objects/processes/login.process'; +import HomePage from '../../page-objects/pages/homepage'; +import SendTokenPage from '../../page-objects/pages/send/send-token-page'; + +describe('ENS', function (this: Suite) { + const sampleAddress: string = '1111111111111111111111111111111111111111'; + + // Having 2 versions of the address is a bug(#25286) + const shortSampleAddress = '0x1111...1111'; + const shortSampleAddresV2 = '0x11111...11111'; + + const sampleEnsDomain: string = 'test.eth'; + const infuraUrl: string = + 'https://mainnet.infura.io/v3/00000000000000000000000000000000'; + + async function mockInfura(mockServer: MockttpServer): Promise { + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_blockNumber' }) + .thenCallback(() => ({ + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x1', + }, + })); + + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_getBalance' }) + .thenCallback(() => ({ + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: '0x1', + }, + })); + + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_getBlockByNumber' }) + .thenCallback(() => ({ + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: {}, + }, + })); + + await mockServer + .forPost(infuraUrl) + .withJsonBodyIncluding({ method: 'eth_call' }) + .thenCallback(() => ({ + statusCode: 200, + json: { + jsonrpc: '2.0', + id: '1111111111111111', + result: `0x000000000000000000000000${sampleAddress}`, + }, + })); + } + + it('domain resolves to a correct address', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().withNetworkControllerOnMainnet().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + testSpecificMock: mockInfura, + }, + async ({ driver }: { driver: Driver }) => { + await loginWithBalanceValidaiton(driver, WALLET_PASSWORD, '<0.000001'); + + // click send button on homepage to start send process + await new HomePage(driver).startSendFlow(); + + // fill ens address as recipient when user lands on send token screen + const sendToPage = new SendTokenPage(driver); + await sendToPage.check_pageIsLoaded(); + await sendToPage.fillRecipient(sampleEnsDomain); + + // verify that ens domain resolves to the correct address + await sendToPage.check_ensAddressResolution( + sampleEnsDomain, + shortSampleAddress, + ); + + // Verify the resolved ENS address can be used as the recipient address + await sendToPage.check_ensAddressAsRecipient( + sampleEnsDomain, + shortSampleAddresV2, + ); + }, + ); + }); +}); diff --git a/test/e2e/tests/transaction/simple-send.spec.js b/test/e2e/tests/transaction/simple-send.spec.js deleted file mode 100644 index 54e14af03a2b..000000000000 --- a/test/e2e/tests/transaction/simple-send.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -const { - defaultGanacheOptions, - withFixtures, - sendTransaction, - logInWithBalanceValidation, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); - -describe('Simple send', function () { - it('can send a simple transaction from one account to another', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver, ganacheServer }) => { - await logInWithBalanceValidation(driver, ganacheServer); - - await sendTransaction( - driver, - '0x985c30949c92df7a0bd42e0f3e3d539ece98db24', - '1', - ); - }, - ); - }); -}); diff --git a/test/e2e/tests/transaction/simple-send.spec.ts b/test/e2e/tests/transaction/simple-send.spec.ts new file mode 100644 index 000000000000..b92241cc47a5 --- /dev/null +++ b/test/e2e/tests/transaction/simple-send.spec.ts @@ -0,0 +1,32 @@ +import { Suite } from 'mocha'; +import { Driver } from '../../webdriver/driver'; +import { withFixtures, defaultGanacheOptions } from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { loginWithBalanceValidaiton } from '../../page-objects/processes/login.process'; +import { sendTransaction } from '../../page-objects/processes/send-transaction.process'; +import HomePage from '../../page-objects/pages/homepage'; + +describe('Simple send eth', function (this: Suite) { + it('can send a simple transaction from one account to another', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await loginWithBalanceValidaiton(driver); + await sendTransaction( + driver, + '0x985c30949c92df7a0bd42e0f3e3d539ece98db24', + '1', + '0.000042', + '1.000042', + ); + const homePage = new HomePage(driver); + await homePage.check_confirmedTxNumberDisplayedInActivity(); + await homePage.check_txAmountInActivity(); + }, + ); + }); +}); diff --git a/test/e2e/tests/transaction/stuck-approved-transaction.spec.js b/test/e2e/tests/transaction/stuck-approved-transaction.spec.js deleted file mode 100644 index 2d50a0d651c1..000000000000 --- a/test/e2e/tests/transaction/stuck-approved-transaction.spec.js +++ /dev/null @@ -1,40 +0,0 @@ -const { strict: assert } = require('assert'); -const { - withFixtures, - unlockWallet, - generateGanacheOptions, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); - -describe('Editing Confirm Transaction', function () { - it('approves a transaction stuck in approved state on boot', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder() - .withTransactionControllerApprovedTransaction() - .build(), - ganacheOptions: generateGanacheOptions({ hardfork: 'london' }), - title: this.test.fullTitle(), - }, - async ({ driver }) => { - await unlockWallet(driver); - - await driver.clickElement( - '[data-testid="account-overview__activity-tab"]', - ); - await driver.wait(async () => { - const confirmedTxes = await driver.findElements( - '.transaction-list__completed-transactions .activity-list-item', - ); - return confirmedTxes.length === 1; - }, 10000); - - const txValues = await driver.findElements( - '[data-testid="transaction-list-item-primary-currency"]', - ); - assert.equal(txValues.length, 1); - assert.ok(/-1\s*ETH/u.test(await txValues[0].getText())); - }, - ); - }); -}); diff --git a/test/e2e/tests/transaction/stuck-approved-transaction.spec.ts b/test/e2e/tests/transaction/stuck-approved-transaction.spec.ts new file mode 100644 index 000000000000..11a5159fc106 --- /dev/null +++ b/test/e2e/tests/transaction/stuck-approved-transaction.spec.ts @@ -0,0 +1,28 @@ +import { Suite } from 'mocha'; +import { withFixtures, generateGanacheOptions } from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { Driver } from '../../webdriver/driver'; +import { loginWithBalanceValidaiton } from '../../page-objects/processes/login.process'; +import HomePage from '../../page-objects/pages/homepage'; + +describe('Editing Confirm Transaction', function (this: Suite) { + it('approves a transaction stuck in approved state on boot', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withTransactionControllerApprovedTransaction() + .build(), + ganacheOptions: generateGanacheOptions({ hardfork: 'london' }), + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await loginWithBalanceValidaiton(driver); + + const homePage = new HomePage(driver); + await homePage.goToActivityList(); + await homePage.check_completedTxNumberDisplayedInActivity(); + await homePage.check_txAmountInActivity(); + }, + ); + }); +}); diff --git a/test/e2e/webdriver/README.md b/test/e2e/webdriver/README.md index 39d7e01fd348..02e0bf25859a 100644 --- a/test/e2e/webdriver/README.md +++ b/test/e2e/webdriver/README.md @@ -543,6 +543,33 @@ This organization helps provide a clear structure for understanding the various > ``` > +
waitForMultipleSelectors + +> **`waitForMultipleSelectors`** function is designed for scenarios where you need to wait for multiple elements to either become visible or be detached from the DOM before proceeding with further actions. It enhances test robustness by allowing for simultaneous waits on several conditions, making it particularly useful in complex web interactions, such as: +> +> - Waiting for all parts of a page to load before performing a comprehensive test. +> - Ensuring multiple UI components are removed after an action. +> +> [source](https://github.com/MetaMask/metamask-extension/blob/671c9975424a83904a4752dfb8a7cf728ae67355/test/e2e/webdriver/driver.js#L357) +> +> #### Arguments +> +> @param `{Array}` rawLocators - Array of element locators
+> @param `{number}` timeout - Optional parameter that specifies the maximum amount of time (in milliseconds) to wait for the condition to be met and desired state of the elements to wait for.
+> It defaults to 'visible', indicating that the function will wait until the elements are visible on the page.
+> The other supported state is 'detached', which means waiting until the elements are removed from the DOM. +> +> #### Returns +> @returns `{Promise>}` Promise resolving when all elements meet the state or timeout occurs.
+> @throws `{Error}` Will throw an error if any of the elements do not reach the specified state within the timeout period. + +> **Example** wait for multiple elements to load +> +> ```jsx +> await driver.waitForMultipleSelectors(['.selector1', '.selector2', '.selector3']); +> ``` +> +
waitForNonEmptyElement > **`waitForNonEmptyElement`** function is an asynchronous function designed to wait until a specified web element contains some text, i.e., it's not empty. This can be particularly useful in scenarios where the content of an element is dynamically loaded or updated, and you need to ensure the element has content before proceeding with further actions. This function is useful when you need to wait for a message, label, or any piece of information to appear in a UI element before performing further actions, such as: diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 5b76eb0d1c91..44ef803d4aef 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -343,6 +343,27 @@ class Driver { return wrapElementWithAPI(element, this); } + /** + * Waits for multiple elements that match the given locators to reach the specified state within the timeout period. + * + * @param {Array} rawLocators - Array of element locators + * @param {number} timeout - Optional parameter that specifies the maximum amount of time (in milliseconds) + * to wait for the condition to be met and desired state of the elements to wait for. + * It defaults to 'visible', indicating that the method will wait until the elements are visible on the page. + * The other supported state is 'detached', which means waiting until the elements are removed from the DOM. + * @returns {Promise>} Promise resolving when all elements meet the state or timeout occurs. + * @throws {Error} Will throw an error if any of the elements do not reach the specified state within the timeout period. + */ + async waitForMultipleSelectors( + rawLocators, + { timeout = this.timeout, state = 'visible' } = {}, + ) { + const promises = rawLocators.map((rawLocator) => + this.waitForSelector(rawLocator, { timeout, state }), + ); + return Promise.all(promises); + } + /** * Waits for an element that matches the given locator to become non-empty within the timeout period. * This is particularly useful for waiting for elements that are dynamically populated with content. diff --git a/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx b/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx index b320a4850cd8..54d80a4e6c5a 100644 --- a/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx +++ b/ui/components/multichain/pages/send/components/domain-input-resolution-cell.tsx @@ -165,7 +165,10 @@ export const DomainInputResolutionCell = ({ )} {ellipsify(address)} - + {domainName && ( - + {ellipsify(address)} )} diff --git a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js index ca9e80fed643..2ed27fe5ced9 100644 --- a/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirmations/confirm-transaction-base/confirm-transaction-base.component.js @@ -585,7 +585,10 @@ export default class ConfirmTransactionBase extends Component { })} subTitle={t('transactionDetailGasTotalSubtitle')} subText={ -
+
diff --git a/ui/pages/confirmations/send/send-content/add-recipient/domain-input.component.js b/ui/pages/confirmations/send/send-content/add-recipient/domain-input.component.js index bb6e432f1a5d..1496f8251812 100644 --- a/ui/pages/confirmations/send/send-content/add-recipient/domain-input.component.js +++ b/ui/pages/confirmations/send/send-content/add-recipient/domain-input.component.js @@ -126,7 +126,10 @@ export default class DomainInput extends Component { > {hasSelectedAddress ? ( <> -
+
Date: Tue, 2 Jul 2024 12:27:24 +0200 Subject: [PATCH 05/10] fix: page object selector not found (#25624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** The e2e that are using page objects are failing due to this error: `Waiting for element to be located By(xpath, .//*[contains(concat(' ', normalize-space(./@class), ' '), ' unlock-page__title ')][(contains(string(.), 'Welcome back!') or contains(string(.), 'Welcomeback!'))])`. It looks like the selector is not correct. This PR uses the data test id to find the correct element. Explanation: [this PR](https://github.com/MetaMask/metamask-extension/pull/25227) removed the css selector, and [this other PR](https://github.com/MetaMask/metamask-extension/pull/25373) implemented page objects with the old selector -possibly the last PR was not updated, so the tests were green, but once the 2 have been merged, now this selector is not found and fails in the subsequent branches ci failure [here](https://app.circleci.com/pipelines/github/MetaMask/metamask-extension/90108/workflows/5d04a531-048a-40e8-87f2-1d4d02f51291/jobs/3338274/tests#failed-test-0) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25624?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ![Screenshot from 2024-07-02 11-48-09](https://github.com/MetaMask/metamask-extension/assets/54408225/faf2a546-d8af-4ed2-884c-85c836994d86) ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- test/e2e/page-objects/pages/login-page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/page-objects/pages/login-page.ts b/test/e2e/page-objects/pages/login-page.ts index 2a3145ccdab5..b96600675040 100644 --- a/test/e2e/page-objects/pages/login-page.ts +++ b/test/e2e/page-objects/pages/login-page.ts @@ -14,7 +14,7 @@ class LoginPage { this.passwordInput = '[data-testid="unlock-password"]'; this.unlockButton = '[data-testid="unlock-submit"]'; this.welcomeBackMessage = { - css: '.unlock-page__title', + css: '[data-testid="unlock-page-title"]', text: 'Welcome back!', }; } From 03fc8a9d0d9267ed3dca9bd7a81c42936a110d56 Mon Sep 17 00:00:00 2001 From: chloeYue <105063779+chloeYue@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:29:33 +0200 Subject: [PATCH 06/10] chore: [Delivery] Update author mapping list for PR (#25606) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Update author team mapping list for PR [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25606?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** Check new authur/team mapping ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- development/generate-rc-commits.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/development/generate-rc-commits.js b/development/generate-rc-commits.js index 4de83ccaebce..a09103c35db9 100644 --- a/development/generate-rc-commits.js +++ b/development/generate-rc-commits.js @@ -49,17 +49,16 @@ const authorTeams = { 'Niranjana Binoy', 'Victor Thomas', 'vthomas13', + 'seaona', + 'Norbert Elter', ], - DappAPI: ['tmashuang', 'jiexi', 'BelfordZ', 'Shane'], - 'Confirmation UX': [ + 'Wallet API': ['tmashuang', 'jiexi', 'BelfordZ', 'Shane'], + Confirmations: [ 'Pedro Figueiredo', 'Sylva Elendu', 'Olusegun Akintayo', 'Jyoti Puri', 'Ariella Vu', - 'seaona', - ], - 'Confirmation Systems': [ 'OGPoyraz', 'vinistevam', 'Matthew Walsh', @@ -67,8 +66,14 @@ const authorTeams = { 'Vinicius Stevam', 'Derek Brans', 'sleepytanya', + 'Priya', + ], + 'Design Systems': [ + 'georgewrmarshall', + 'Garrett Bear', + 'George Marshall', + 'Devin', ], - 'Design Systems': ['georgewrmarshall', 'Garrett Bear', 'George Marshall'], Snaps: [ 'David Drazic', 'hmalik88', @@ -84,7 +89,7 @@ const authorTeams = { Assets: ['salimtb', 'sahar-fehri', 'Brian Bergeron'], Linea: ['VGau', 'Victorien Gauch'], lavamoat: ['weizman', 'legobeat', 'kumavis', 'LeoTM'], - 'Shared Libraries': [ + 'Wallet Framework': [ 'Michele Esposito', 'Elliot Winkler', 'Gudahtt', @@ -98,10 +103,11 @@ const authorTeams = { 'Shane T', 'Bernardo Garces Chapero', ], - Swaps: ['Daniel', 'Davide Brocchetto', 'Nicolas Ferro'], + Swaps: ['Daniel', 'Davide Brocchetto', 'Nicolas Ferro', 'infiniteflower'], Devex: ['Thomas Huang', 'Alex Donesky', 'jiexi', 'Zachary Belford'], Notifications: ['Prithpal-Sooriya', 'Matteo Scurati', 'Prithpal Sooriya'], - Bridging: ['Bilal', 'micaelae'], + Bridging: ['Bilal', 'micaelae', 'Ethan Wessel'], + Ramps: ['George Weiler'], }; // Function to get PR labels From d721e7f7b1d7a915624fb14050a8b4037c524d96 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Tue, 2 Jul 2024 20:17:29 +0800 Subject: [PATCH 07/10] fix: account missing in connection page (#25500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR fixes a regression where the connected account is not showing up on the page. ## **Related issues** ## **Manual testing steps** 1. Go to a test dapp 2. Connect the account 3. Open the popup 4. Click on the connection icon and see the connected account. ## **Screenshots/Recordings** ### **Before** Screenshot 2024-06-25 at 15 30 55 ### **After** Screenshot 2024-06-25 at 15 31 01 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Nidhi Kumari Co-authored-by: Charly Chevalier --- test/e2e/helpers.js | 9 +++++ .../tests/multichain/connection-page.spec.js | 33 +++++++++++++++++++ .../account-list-item.test.js.snap | 2 ++ .../account-list-item/account-list-item.js | 6 +++- .../app-header-unlocked-content.tsx | 2 ++ .../connected-site-menu.js | 2 +- .../__snapshots__/connections.test.tsx.snap | 1 + .../send/__snapshots__/send.test.js.snap | 1 + .../__snapshots__/your-accounts.test.tsx.snap | 6 ++++ .../remove-snap-account.test.js.snap | 1 + 10 files changed, 61 insertions(+), 2 deletions(-) diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index 226fe4f9f6ac..dbdfdeb2446d 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -675,6 +675,14 @@ const openDapp = async (driver, contract = null, dappURL = DAPP_URL) => { : await driver.openNewPage(dappURL); }; +const openDappConnectionsPage = async (driver) => { + await driver.openNewPage( + `${driver.extensionUrl}/home.html#connections/${encodeURIComponent( + DAPP_URL, + )}`, + ); +}; + const createDappTransaction = async (driver, transaction) => { await openDapp( driver, @@ -1158,6 +1166,7 @@ module.exports = { importWrongSRPOnboardingFlow, testSRPDropdownIterations, openDapp, + openDappConnectionsPage, createDappTransaction, switchToOrOpenDapp, connectToDapp, diff --git a/test/e2e/tests/multichain/connection-page.spec.js b/test/e2e/tests/multichain/connection-page.spec.js index e5594de82840..2d1d2f2d106c 100644 --- a/test/e2e/tests/multichain/connection-page.spec.js +++ b/test/e2e/tests/multichain/connection-page.spec.js @@ -183,4 +183,37 @@ describe('Connections page', function () { }, ); }); + + // Skipped until issue where firefox connecting to dapp is resolved. + // it('shows that the account is connected to the dapp', async function () { + // await withFixtures( + // { + // dapp: true, + // fixtures: new FixtureBuilder().build(), + // title: this.test.fullTitle(), + // ganacheOptions: defaultGanacheOptions, + // }, + // async ({ driver, ganacheServer }) => { + // const ACCOUNT = '0x5CfE73b6021E818B776b421B1c4Db2474086a7e1'; + // const SHORTENED_ACCOUNT = shortenAddress(ACCOUNT); + // await logInWithBalanceValidation(driver, ganacheServer); + // await openDappConnectionsPage(driver); + // // Verify that there are no connected accounts + // await driver.assertElementNotPresent( + // '[data-testid="account-list-address"]', + // ); + + // await connectToDapp(driver); + // await openDappConnectionsPage(driver); + + // const account = await driver.findElement( + // '[data-testid="account-list-address"]', + // ); + // const accountAddress = await account.getText(); + + // // Dapp should contain single connected account address + // assert.strictEqual(accountAddress, SHORTENED_ACCOUNT); + // }, + // ); + // }); }); diff --git a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap index 0b2b13a29277..ae53a1353084 100644 --- a/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap +++ b/ui/components/multichain/account-list-item/__snapshots__/account-list-item.test.js.snap @@ -262,6 +262,7 @@ exports[`AccountListItem renders AccountListItem component and shows account nam >

bc1qn3s...5eker

@@ -557,6 +558,7 @@ exports[`AccountListItem renders AccountListItem component and shows account nam >

0x0DCD5...3E7bc

diff --git a/ui/components/multichain/account-list-item/account-list-item.js b/ui/components/multichain/account-list-item/account-list-item.js index c3e09207cb7a..2b48bcb79baf 100644 --- a/ui/components/multichain/account-list-item/account-list-item.js +++ b/ui/components/multichain/account-list-item/account-list-item.js @@ -320,7 +320,11 @@ export const AccountListItem = ({ justifyContent={JustifyContent.spaceBetween} > - + {shortenAddress(normalizeSafeAddress(account.address))} diff --git a/ui/components/multichain/app-header/app-header-unlocked-content.tsx b/ui/components/multichain/app-header/app-header-unlocked-content.tsx index 0cca531b5fc1..2be2d5ffd342 100644 --- a/ui/components/multichain/app-header/app-header-unlocked-content.tsx +++ b/ui/components/multichain/app-header/app-header-unlocked-content.tsx @@ -41,6 +41,7 @@ import { GlobalMenu } from '../global-menu'; import { getSelectedInternalAccount, getTestNetworkBackgroundColor, + getOriginOfCurrentTab, } from '../../../selectors'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { normalizeSafeAddress } from '../../../../app/scripts/lib/multichain/address'; @@ -80,6 +81,7 @@ export const AppHeaderUnlockedContent = ({ const t = useI18nContext(); const history = useHistory(); const dispatch = useDispatch(); + const origin = useSelector(getOriginOfCurrentTab); const [accountOptionsMenuOpen, setAccountOptionsMenuOpen] = useState(false); const testNetworkBackgroundColor = useSelector(getTestNetworkBackgroundColor); diff --git a/ui/components/multichain/connected-site-menu/connected-site-menu.js b/ui/components/multichain/connected-site-menu/connected-site-menu.js index c7811b3e819c..4f532710288e 100644 --- a/ui/components/multichain/connected-site-menu/connected-site-menu.js +++ b/ui/components/multichain/connected-site-menu/connected-site-menu.js @@ -137,5 +137,5 @@ ConnectedSiteMenu.propTypes = { /** * Disable the connected site menu if the account is non-evm */ - disabled: PropTypes.boolean, + disabled: PropTypes.bool, }; diff --git a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap index faa7a4fac173..3864de9c921e 100644 --- a/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap +++ b/ui/components/multichain/pages/connections/__snapshots__/connections.test.tsx.snap @@ -317,6 +317,7 @@ exports[`Connections Content should render correctly 1`] = ` >

0x0DCD5...3E7bc

diff --git a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap index 59f5db9f8385..a3ee231d6ae2 100644 --- a/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap +++ b/ui/components/multichain/pages/send/__snapshots__/send.test.js.snap @@ -489,6 +489,7 @@ exports[`SendPage render and initialization should render correctly even when a >

0x0

diff --git a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap index 411ee70e4ea3..901e7def9c2c 100644 --- a/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap +++ b/ui/components/multichain/pages/send/components/__snapshots__/your-accounts.test.tsx.snap @@ -265,6 +265,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` >

0x0DCD5...3E7bc

@@ -561,6 +562,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` >

0xEC1Ad...9251B

@@ -857,6 +859,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` >

0xc42ED...D8813

@@ -1162,6 +1165,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` >

0xeB9e6...64823

@@ -1458,6 +1462,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` >

0xb5526...FEe5D

@@ -1767,6 +1772,7 @@ exports[`SendPageYourAccounts render renders correctly 1`] = ` >

0xca8f1...Cf281

diff --git a/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap b/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap index 7a411efb53ed..e0305e1c6533 100644 --- a/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap +++ b/ui/pages/confirmations/confirmation/templates/__snapshots__/remove-snap-account.test.js.snap @@ -361,6 +361,7 @@ exports[`remove-snap-account confirmation should match snapshot 1`] = ` >

0x0DCD5...3E7bc

From 4886a3ff5ce5b8da4c313b888b77b0ec47de93d5 Mon Sep 17 00:00:00 2001 From: seaona <54408225+seaona@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:18:46 +0200 Subject: [PATCH 08/10] chore: exclude running git diff job for the e2e quality gate in `develop`, `master` and release branches (#25605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR excludes running the git-diff script for the e2e quality gate added [here](https://github.com/MetaMask/metamask-extension/commit/3ea381d26963849a33d76d5b644c5f0044c9c778), for `develop` as well as for `master` and release branches. We don't want to run this in develop, master or release branch, since there is no point on re-running the new/changed tests there, as the changes are already in `develop` branch. This could add up more credits and also slow down ci in those branches. **Context**: in `develop` branch the current quality gate fails, since the `diffResult` is empty, and we were throwing the following [error](https://app.circleci.com/pipelines/github/MetaMask/metamask-extension/89939/workflows/dccfb6e0-741d-4416-bc01-8ab1a884c12b/jobs/3329380) as we were entering in the !diffResult condition. However, this PR fixes the issue on the higher level, by skipping entirely the git diff for the mentioned branches above. ``` await fetchUntilMergeBaseFound(); const { stdout: diffResult } = await exec(`git diff --name-only origin/HEAD...${process.env.CIRCLE_BRANCH}`); if (!diffResult) { throw new Error('Unable to get diff after full checkout.'); } ``` [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25605?quickstart=1) ## **Related issues** Fixes: current develop ci ## **Manual testing steps** - ci should continue to work - once merged, fixes develop ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .circleci/config.yml | 12 +++++++++--- test/e2e/changedFilesUtil.js | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d9f0754335e7..aaed8f3c8bf2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -53,6 +53,13 @@ develop_master_rc_only: &develop_master_rc_only - master - /^Version-v(\d+)[.](\d+)[.](\d+)/ +exclude_develop_master_rc: &exclude_develop_master_rc + filters: + branches: + ignore: + - develop + - master + - /^Version-v(\d+)[.](\d+)[.](\d+)/ aliases: # Shallow Git Clone - &shallow-git-clone @@ -106,6 +113,7 @@ workflows: - check-pr-tag - prep-deps - get-changed-files-with-git-diff: + <<: *exclude_develop_master_rc requires: - prep-deps - test-deps-audit: @@ -203,7 +211,6 @@ workflows: <<: *develop_master_rc_only requires: - prep-build-confirmation-redesign-test-mv2 - - get-changed-files-with-git-diff - test-e2e-chrome-rpc: requires: - prep-build-test @@ -223,7 +230,6 @@ workflows: <<: *develop_master_rc_only requires: - prep-build-test-flask-mv2 - - get-changed-files-with-git-diff - test-e2e-chrome-mmi: requires: - prep-build-test-mmi @@ -243,7 +249,6 @@ workflows: - /^Version-v(\d+)[.](\d+)[.](\d+)/ requires: - prep-build - - get-changed-files-with-git-diff - test-unit-jest-main: requires: - prep-deps @@ -488,6 +493,7 @@ jobs: # This job is used for the e2e quality gate. # It must be run before any job which uses the run-all.js script. + # The job is skipped in develop, master or RC branches. get-changed-files-with-git-diff: executor: node-browsers-small steps: diff --git a/test/e2e/changedFilesUtil.js b/test/e2e/changedFilesUtil.js index 5ead76203db0..7beb7c94e9e7 100644 --- a/test/e2e/changedFilesUtil.js +++ b/test/e2e/changedFilesUtil.js @@ -20,7 +20,7 @@ async function readChangedFiles() { return changedFiles; } catch (error) { console.error('Error reading from file:', error); - return ''; + return []; } } From c6419813d24772e369f4e73ccad67c8e13abbf8d Mon Sep 17 00:00:00 2001 From: Mike B <32695229+plasmacorral@users.noreply.github.com> Date: Tue, 2 Jul 2024 08:30:28 -0400 Subject: [PATCH 09/10] test: add e2e to swap with snap account (#25558) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25558?quickstart=1) ## **Related issues** Fixes: [469](https://github.com/MetaMask/accounts-planning/issues/469) ## **Manual testing steps** 1. `yarn build:test:mv2` then `ENABLE_MV3=false yarn test:e2e:single test/e2e/accounts/snap-account-eth-swap.spec.ts --browser=firefox` 2. `yarn build:test` then `yarn test:e2e:single test/e2e/accounts/snap-account-eth-swap.spec.ts --browser=chrome` ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../accounts/snap-account-eth-swap.spec.ts | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 test/e2e/accounts/snap-account-eth-swap.spec.ts diff --git a/test/e2e/accounts/snap-account-eth-swap.spec.ts b/test/e2e/accounts/snap-account-eth-swap.spec.ts new file mode 100644 index 000000000000..bb4c3c7a5770 --- /dev/null +++ b/test/e2e/accounts/snap-account-eth-swap.spec.ts @@ -0,0 +1,48 @@ +import { withFixtures, defaultGanacheOptions, WINDOW_TITLES } from '../helpers'; +import { Driver } from '../webdriver/driver'; +import FixtureBuilder from '../fixture-builder'; +import { + buildQuote, + reviewQuote, + waitForTransactionToComplete, + checkActivityTransaction, +} from '../tests/swaps/shared'; +import { installSnapSimpleKeyring } from './common'; + +const DAI = 'DAI'; +const TEST_ETH = 'TESTETH'; + +describe('Snap Account - Swap', function () { + it('swaps ETH for DAI using a snap account', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + }, + async ({ driver }: { driver: Driver }) => { + await installSnapSimpleKeyring(driver, false); + await driver.switchToWindowWithTitle( + WINDOW_TITLES.ExtensionInFullScreenView, + ); + await buildQuote(driver, { + amount: 0.001, + swapTo: DAI, + }); + await reviewQuote(driver, { + amount: 0.001, + swapFrom: TEST_ETH, + swapTo: DAI, + }); + await driver.clickElement({ text: 'Swap', tag: 'button' }); + await waitForTransactionToComplete(driver, { tokenName: 'DAI' }); + await checkActivityTransaction(driver, { + index: 0, + amount: '0.001', + swapFrom: TEST_ETH, + swapTo: DAI, + }); + }, + ); + }); +}); From 7d94797c50e8bde272b2f6083981517fdacfd7be Mon Sep 17 00:00:00 2001 From: Xiaoming Wang <7315988+dawnseeker8@users.noreply.github.com> Date: Tue, 2 Jul 2024 13:59:30 +0100 Subject: [PATCH 10/10] fix: Fix issue 22837 about unknown error during ledger pair (#25462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Issue #22837 descrip that ledger sometimes will display `Unknown Error` during pairing. we have tried to replicate the issue and discover that it happen during ledger is lock and not open Eth app. this PR will replace the `Unknown Error` with more meaningful error message to guide user solve the issue. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25462?quickstart=1) ## **Related issues** Fixes: #22837 ## **Manual testing steps** 1. Setup wallet 2. Connect, unlock and open the Ethereum app on Ledger 3. Use the add hardware flow to add some Ledger accounts 4. Remove the Ledger accounts from full screen mode 5. Use add hardware flow to try and add any Ledger accounts 6. Select the `paired` with that ledger when it is available 7. ledger should be locked status or unlock ledger but not opening eth app. 8. you will see new error message `Unlock your Ledger device and open the ETH app` 9. After you unlock your ledger and open ETH app. and click paired again 10. you should be able to select accounts from next screen and import those accounts from ledger. ## **Screenshots/Recordings** ### **Before** https://recordit.co/kdCDL4laWo ### **After** https://github.com/MetaMask/metamask-extension/assets/7315988/f11a1867-ad3f-4796-8661-6d3469bed682 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: chloeYue <105063779+chloeYue@users.noreply.github.com> --- ...r-bridge-keyring-npm-2.0.1-7a5d815b2d.patch | 18 ++++++++++++++++++ package.json | 2 +- yarn.lock | 17 +++++++++++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 .yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch diff --git a/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch b/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch new file mode 100644 index 000000000000..786d5cd1b226 --- /dev/null +++ b/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch @@ -0,0 +1,18 @@ +diff --git a/dist/ledger-keyring.js b/dist/ledger-keyring.js +index 2386b2e7fe36d1e65ef74f0a19d3b41450dcfa48..f999a0ab465cce7a450a5812f1d7aa6e39b74aed 100644 +--- a/dist/ledger-keyring.js ++++ b/dist/ledger-keyring.js +@@ -150,7 +150,12 @@ class LedgerKeyring extends events_1.EventEmitter { + }); + } + catch (error) { +- throw error instanceof Error ? error : new Error('Unknown error'); ++ ++ /** ++ * For Fixing issue 22837, when ledger is locked and didnt open the ethereum app in ledger, ++ * The extension will always show `unknown error`, below change will transform the error to something meaningful. ++ */ ++ throw error instanceof Error ? error : new Error('Unlock your Ledger device and open the ETH app'); + } + if (updateHdk && payload.chainCode) { + this.hdk.publicKey = buffer_1.Buffer.from(payload.publicKey, 'hex'); diff --git a/package.json b/package.json index f9dbb8dbeb77..391ffff36e58 100644 --- a/package.json +++ b/package.json @@ -298,7 +298,7 @@ "@metamask/ens-controller": "^10.0.1", "@metamask/eth-json-rpc-filters": "^7.0.0", "@metamask/eth-json-rpc-middleware": "^12.1.1", - "@metamask/eth-ledger-bridge-keyring": "^2.0.1", + "@metamask/eth-ledger-bridge-keyring": "patch:@metamask/eth-ledger-bridge-keyring@npm%3A2.0.1#~/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch", "@metamask/eth-query": "^4.0.0", "@metamask/eth-sig-util": "^7.0.1", "@metamask/eth-snap-keyring": "^4.3.1", diff --git a/yarn.lock b/yarn.lock index 1b0c258c8e28..87e407e69b99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5211,7 +5211,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-ledger-bridge-keyring@npm:^2.0.1": +"@metamask/eth-ledger-bridge-keyring@npm:2.0.1": version: 2.0.1 resolution: "@metamask/eth-ledger-bridge-keyring@npm:2.0.1" dependencies: @@ -5224,6 +5224,19 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-ledger-bridge-keyring@patch:@metamask/eth-ledger-bridge-keyring@npm%3A2.0.1#~/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch": + version: 2.0.1 + resolution: "@metamask/eth-ledger-bridge-keyring@patch:@metamask/eth-ledger-bridge-keyring@npm%3A2.0.1#~/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch::version=2.0.1&hash=322aa0" + dependencies: + "@ethereumjs/rlp": "npm:^4.0.0" + "@ethereumjs/tx": "npm:^4.1.1" + "@ethereumjs/util": "npm:^8.0.0" + "@metamask/eth-sig-util": "npm:^7.0.0" + hdkey: "npm:^2.1.0" + checksum: 10/0f8c86d1b4c323b8a79fa82e3df300034f8dea928569cc3560d1b3352e09e9397844b75b8642ca57866ef5e241b49bd190b8ba7d1b08efa248d9ca909485a674 + languageName: node + linkType: hard + "@metamask/eth-query@npm:^3.0.1": version: 3.0.1 resolution: "@metamask/eth-query@npm:3.0.1" @@ -25188,7 +25201,7 @@ __metadata: "@metamask/eslint-plugin-design-tokens": "npm:^1.1.0" "@metamask/eth-json-rpc-filters": "npm:^7.0.0" "@metamask/eth-json-rpc-middleware": "npm:^12.1.1" - "@metamask/eth-ledger-bridge-keyring": "npm:^2.0.1" + "@metamask/eth-ledger-bridge-keyring": "patch:@metamask/eth-ledger-bridge-keyring@npm%3A2.0.1#~/.yarn/patches/@metamask-eth-ledger-bridge-keyring-npm-2.0.1-7a5d815b2d.patch" "@metamask/eth-query": "npm:^4.0.0" "@metamask/eth-sig-util": "npm:^7.0.1" "@metamask/eth-snap-keyring": "npm:^4.3.1"