From a33f52631f87275f86459b74d0e7ae12511b954c Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 20 Dec 2024 15:26:18 +0530 Subject: [PATCH 1/9] fix: UI is not displaying gas limit set by dapp (#29352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** UI is not displaying correct gas limit set by dapp. It always displays `21000`. ## **Related issues** Fixes: https://github.com/MetaMask/metamask-extension/issues/18417 ## **Manual testing steps** 1. Submit confirmation request with gas limit not `21000` 2. Check gas limit in gas editing popup ## **Screenshots/Recordings** Screenshot 2024-12-19 at 7 20 45 PM ## **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/main/.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/main/.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. --- ui/pages/confirmations/hooks/useGasFeeInputs.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ui/pages/confirmations/hooks/useGasFeeInputs.js b/ui/pages/confirmations/hooks/useGasFeeInputs.js index 8675b726038a..129778bfcf20 100644 --- a/ui/pages/confirmations/hooks/useGasFeeInputs.js +++ b/ui/pages/confirmations/hooks/useGasFeeInputs.js @@ -164,7 +164,11 @@ export function useGasFeeInputs( }); const [gasLimit, setGasLimit] = useState(() => - Number(hexToDecimal(transaction?.txParams?.gas ?? '0x0')), + Number( + hexToDecimal( + transaction?.txParams?.gasLimit ?? transaction?.txParams?.gas ?? '0x0', + ), + ), ); const properGasLimit = Number(hexToDecimal(transaction?.originalGasEstimate)); @@ -195,7 +199,15 @@ export function useGasFeeInputs( setEstimateUsed(transaction?.userFeeLevel); } - setGasLimit(Number(hexToDecimal(transaction?.txParams?.gas ?? '0x0'))); + setGasLimit( + Number( + hexToDecimal( + transaction?.txParams?.gasLimit ?? + transaction?.txParams?.gas ?? + '0x0', + ), + ), + ); } }, [ setEstimateUsed, From 356ad476f75183e17f9493b75625575daedfd018 Mon Sep 17 00:00:00 2001 From: chloeYue <105063779+chloeYue@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:38:04 +0100 Subject: [PATCH 2/9] test: [POM] Migrate watch account tests (#29314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** - Created a new page class `AccountDetailsModal`. Previously, it was a part of `AccountList`. I think it's better to separate it and make it an independent class. - I also took the chance to improve the function `addAccount` and remove the origin `addNewAccount`. So now for creating ethereum, bitcoin, solana accounts, we use the same `addAccount` function with the account type as a parameter. - Migrate watch account e2e tests to Page Object Model - Created `watchEoaAddress` flow that can be reusable. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27155?quickstart=1) ## **Related issues** ## **Manual testing steps** Check code readability, make sure tests pass. ## **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. --- test/e2e/constants.ts | 7 + test/e2e/flask/btc/common-btc.ts | 4 +- test/e2e/flask/btc/create-btc-account.spec.ts | 37 +- test/e2e/flask/create-watch-account.spec.ts | 407 +++++++----------- test/e2e/flask/solana/common-solana.ts | 7 +- .../solana/create-solana-account.spec.ts | 7 +- test/e2e/page-objects/common.ts | 6 - .../page-objects/flows/watch-account.flow.ts | 22 + .../page-objects/pages/account-list-page.ts | 194 ++++----- .../pages/dialog/account-details-modal.ts | 106 +++++ test/e2e/page-objects/pages/header-navbar.ts | 26 ++ .../pages/home/bitcoin-homepage.ts | 10 +- test/e2e/page-objects/pages/home/homepage.ts | 50 ++- .../pages/settings/experimental-settings.ts | 20 + .../tests/account/account-custom-name.spec.ts | 14 +- test/e2e/tests/account/add-account.spec.ts | 13 +- test/e2e/tests/account/import-flow.spec.ts | 5 +- .../account-syncing/new-user-sync.spec.ts | 6 +- .../onboarding-with-opt-out.spec.ts | 7 +- .../sync-after-adding-account.spec.ts | 10 +- .../sync-after-modifying-account-name.spec.ts | 11 +- .../sync-with-account-balances.spec.ts | 14 +- test/e2e/tests/tokens/nft/import-nft.spec.ts | 5 +- 23 files changed, 559 insertions(+), 429 deletions(-) create mode 100644 test/e2e/page-objects/flows/watch-account.flow.ts create mode 100644 test/e2e/page-objects/pages/dialog/account-details-modal.ts diff --git a/test/e2e/constants.ts b/test/e2e/constants.ts index 9d4d5bbf3c8d..6af1056d7232 100644 --- a/test/e2e/constants.ts +++ b/test/e2e/constants.ts @@ -76,3 +76,10 @@ export const DEFAULT_SOLANA_BALANCE = 1; // SOL /* Title of the mocked E2E test empty HTML page */ export const EMPTY_E2E_TEST_PAGE_TITLE = 'E2E Test Page'; + +/* Account types */ +export enum ACCOUNT_TYPE { + Ethereum, + Bitcoin, + Solana, +} diff --git a/test/e2e/flask/btc/common-btc.ts b/test/e2e/flask/btc/common-btc.ts index db2db85eb554..ea9cb6b1f2e6 100644 --- a/test/e2e/flask/btc/common-btc.ts +++ b/test/e2e/flask/btc/common-btc.ts @@ -2,6 +2,7 @@ import { Mockttp } from 'mockttp'; import FixtureBuilder from '../../fixture-builder'; import { withFixtures } from '../../helpers'; import { + ACCOUNT_TYPE, DEFAULT_BTC_ACCOUNT, DEFAULT_BTC_BALANCE, DEFAULT_BTC_FEES_RATE, @@ -14,7 +15,6 @@ import { Driver } from '../../webdriver/driver'; import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; import AccountListPage from '../../page-objects/pages/account-list-page'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; -import { ACCOUNT_TYPE } from '../../page-objects/common'; const QUICKNODE_URL_REGEX = /^https:\/\/.*\.btc.*\.quiknode\.pro(\/|$)/u; @@ -218,7 +218,7 @@ export async function withBtcAccountSnap( await new HeaderNavbar(driver).openAccountMenu(); const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, ''); + await accountListPage.addAccount({ accountType: ACCOUNT_TYPE.Bitcoin }); await test(driver, mockServer); }, ); diff --git a/test/e2e/flask/btc/create-btc-account.spec.ts b/test/e2e/flask/btc/create-btc-account.spec.ts index f563454ad1e6..68cdcd82caf5 100644 --- a/test/e2e/flask/btc/create-btc-account.spec.ts +++ b/test/e2e/flask/btc/create-btc-account.spec.ts @@ -1,13 +1,14 @@ import { strict as assert } from 'assert'; import { Suite } from 'mocha'; import { WALLET_PASSWORD } from '../../helpers'; +import AccountDetailsModal from '../../page-objects/pages/dialog/account-details-modal'; import AccountListPage from '../../page-objects/pages/account-list-page'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; import LoginPage from '../../page-objects/pages/login-page'; import PrivacySettings from '../../page-objects/pages/settings/privacy-settings'; import ResetPasswordPage from '../../page-objects/pages/reset-password-page'; import SettingsPage from '../../page-objects/pages/settings/settings-page'; -import { ACCOUNT_TYPE } from '../../page-objects/common'; +import { ACCOUNT_TYPE } from '../../constants'; import { withBtcAccountSnap } from './common-btc'; describe('Create BTC Account', function (this: Suite) { @@ -82,9 +83,11 @@ describe('Create BTC Account', function (this: Suite) { await headerNavbar.openAccountMenu(); const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - const accountAddress = await accountListPage.getAccountAddress( - 'Bitcoin Account', - ); + await accountListPage.openAccountDetailsModal('Bitcoin Account'); + + const accountDetailsModal = new AccountDetailsModal(driver); + await accountDetailsModal.check_pageIsLoaded(); + const accountAddress = await accountDetailsModal.getAccountAddress(); await headerNavbar.openAccountMenu(); await accountListPage.removeAccount('Bitcoin Account'); @@ -97,14 +100,15 @@ describe('Create BTC Account', function (this: Suite) { ); await accountListPage.closeAccountModal(); await headerNavbar.openAccountMenu(); - await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, ''); + await accountListPage.addAccount({ accountType: ACCOUNT_TYPE.Bitcoin }); await headerNavbar.check_accountLabel('Bitcoin Account'); await headerNavbar.openAccountMenu(); await accountListPage.check_pageIsLoaded(); - const recreatedAccountAddress = await accountListPage.getAccountAddress( - 'Bitcoin Account', - ); + await accountListPage.openAccountDetailsModal('Bitcoin Account'); + await accountDetailsModal.check_pageIsLoaded(); + const recreatedAccountAddress = + await accountDetailsModal.getAccountAddress(); assert(accountAddress === recreatedAccountAddress); }, @@ -123,9 +127,10 @@ describe('Create BTC Account', function (this: Suite) { await headerNavbar.openAccountMenu(); const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - const accountAddress = await accountListPage.getAccountAddress( - 'Bitcoin Account', - ); + await accountListPage.openAccountDetailsModal('Bitcoin Account'); + const accountDetailsModal = new AccountDetailsModal(driver); + await accountDetailsModal.check_pageIsLoaded(); + const accountAddress = await accountDetailsModal.getAccountAddress(); // go to privacy settings page and get the SRP await headerNavbar.openSettingsPage(); @@ -151,14 +156,16 @@ describe('Create BTC Account', function (this: Suite) { await headerNavbar.check_pageIsLoaded(); await headerNavbar.openAccountMenu(); await accountListPage.check_pageIsLoaded(); - await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, ''); + await accountListPage.addAccount({ accountType: ACCOUNT_TYPE.Bitcoin }); await headerNavbar.check_accountLabel('Bitcoin Account'); await headerNavbar.openAccountMenu(); await accountListPage.check_pageIsLoaded(); - const recreatedAccountAddress = await accountListPage.getAccountAddress( - 'Bitcoin Account', - ); + await accountListPage.openAccountDetailsModal('Bitcoin Account'); + await accountDetailsModal.check_pageIsLoaded(); + const recreatedAccountAddress = + await accountDetailsModal.getAccountAddress(); + assert(accountAddress === recreatedAccountAddress); }, ); diff --git a/test/e2e/flask/create-watch-account.spec.ts b/test/e2e/flask/create-watch-account.spec.ts index f25f38f0b2ce..5cfca1dda5ef 100644 --- a/test/e2e/flask/create-watch-account.spec.ts +++ b/test/e2e/flask/create-watch-account.spec.ts @@ -1,70 +1,22 @@ import { strict as assert } from 'assert'; import { Suite } from 'mocha'; -import messages from '../../../app/_locales/en/messages.json'; import FixtureBuilder from '../fixture-builder'; -import { defaultGanacheOptions, unlockWallet, withFixtures } from '../helpers'; +import { withFixtures } from '../helpers'; import { Driver } from '../webdriver/driver'; +import AccountDetailsModal from '../page-objects/pages/dialog/account-details-modal'; +import AccountListPage from '../page-objects/pages/account-list-page'; +import ExperimentalSettings from '../page-objects/pages/settings/experimental-settings'; +import HeaderNavbar from '../page-objects/pages/header-navbar'; +import HomePage from '../page-objects/pages/home/homepage'; +import SettingsPage from '../page-objects/pages/settings/settings-page'; +import { loginWithBalanceValidation } from '../page-objects/flows/login.flow'; +import { watchEoaAddress } from '../page-objects/flows/watch-account.flow'; const ACCOUNT_1 = '0x5CfE73b6021E818B776b421B1c4Db2474086a7e1'; const EOA_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; const SHORTENED_EOA_ADDRESS = '0xd8dA6...96045'; const DEFAULT_WATCHED_ACCOUNT_NAME = 'Watched Account 1'; -/** - * Start the flow to create a watch account by clicking the account menu and selecting the option to add a watch account. - * - * @param driver - The WebDriver instance used to control the browser. - * @param unlockWalletFirst - Whether to unlock the wallet before starting the flow. - */ -async function startCreateWatchAccountFlow( - driver: Driver, - unlockWalletFirst: boolean = true, -): Promise { - if (unlockWalletFirst) { - await unlockWallet(driver); - } - - await driver.clickElement('[data-testid="account-menu-icon"]'); - await driver.clickElement( - '[data-testid="multichain-account-menu-popover-action-button"]', - ); - await driver.clickElement( - '[data-testid="multichain-account-menu-popover-add-watch-only-account"]', - ); -} - -/** - * Watches an EOA address. - * - * @param driver - The WebDriver instance used to control the browser. - * @param unlockWalletFirst - Whether to unlock the wallet before watching the address. - * @param address - The EOA address to watch. - */ -async function watchEoaAddress( - driver: Driver, - unlockWalletFirst: boolean = true, - address: string = EOA_ADDRESS, -): Promise { - await startCreateWatchAccountFlow(driver, unlockWalletFirst); - await driver.fill('input#address-input[type="text"]', address); - await driver.clickElement({ text: 'Watch account', tag: 'button' }); - await driver.clickElement('[data-testid="submit-add-account-with-name"]'); -} - -/** - * Removes the selected account. - * - * @param driver - The WebDriver instance used to control the browser. - */ -async function removeSelectedAccount(driver: Driver): Promise { - await driver.clickElement('[data-testid="account-menu-icon"]'); - await driver.clickElement( - '.multichain-account-list-item--selected [data-testid="account-list-item-menu-button"]', - ); - await driver.clickElement('[data-testid="account-list-menu-remove"]'); - await driver.clickElement({ text: 'Remove', tag: 'button' }); -} - describe('Account-watcher snap', function (this: Suite) { describe('Adding watched accounts', function () { it('adds watch account with valid EOA address', async function () { @@ -76,22 +28,17 @@ describe('Account-watcher snap', function (this: Suite) { }) .withNetworkControllerOnMainnet() .build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { // watch an EOA address - await watchEoaAddress(driver); + await loginWithBalanceValidation(driver); + await watchEoaAddress(driver, EOA_ADDRESS); // new account should be displayed in the account list - await driver.findElement({ - css: '[data-testid="account-menu-icon"]', - text: DEFAULT_WATCHED_ACCOUNT_NAME, - }); - await driver.findElement({ - css: '.mm-text--ellipsis', - text: SHORTENED_EOA_ADDRESS, - }); + const headerNavbar = new HeaderNavbar(driver); + await headerNavbar.check_accountLabel(DEFAULT_WATCHED_ACCOUNT_NAME); + await headerNavbar.check_accountAddress(SHORTENED_EOA_ADDRESS); }, ); }); @@ -105,40 +52,29 @@ describe('Account-watcher snap', function (this: Suite) { }) .withNetworkControllerOnMainnet() .build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { // watch an EOA address - await watchEoaAddress(driver); + await loginWithBalanceValidation(driver); + await watchEoaAddress(driver, EOA_ADDRESS); + const homePage = new HomePage(driver); + await homePage.headerNavbar.check_accountLabel( + DEFAULT_WATCHED_ACCOUNT_NAME, + ); // 'Send' button should be disabled - await driver.findElement( - '[data-testid="eth-overview-send"][disabled]', - ); - await driver.findElement( - '[data-testid="eth-overview-send"].icon-button--disabled', - ); + assert.equal(await homePage.check_ifSendButtonIsClickable(), false); // 'Swap' button should be disabled - await driver.findElement( - '[data-testid="token-overview-button-swap"][disabled]', - ); - await driver.findElement( - '[data-testid="token-overview-button-swap"].icon-button--disabled', - ); + assert.equal(await homePage.check_ifSwapButtonIsClickable(), false); // 'Bridge' button should be disabled - await driver.findElement( - '[data-testid="eth-overview-bridge"][disabled]', - ); - await driver.findElement( - '[data-testid="eth-overview-bridge"].icon-button--disabled', - ); + assert.equal(await homePage.check_ifBridgeButtonIsClickable(), false); // check tooltips for disabled buttons - await driver.findElement( - '.icon-button--disabled [data-tooltipped][data-original-title="Not supported with this account."]', + await homePage.check_disabledButtonTooltip( + 'Not supported with this account.', ); }, ); @@ -182,20 +118,19 @@ describe('Account-watcher snap', function (this: Suite) { }) .withNetworkControllerOnMainnet() .build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { - await startCreateWatchAccountFlow(driver); - - await driver.fill('input#address-input[type="text"]', input); - await driver.clickElement({ text: 'Watch account', tag: 'button' }); - - // error message should be displayed by the snap - await driver.findElement({ - css: '.snap-ui-renderer__text', - text: message, - }); + await loginWithBalanceValidation(driver); + const homePage = new HomePage(driver); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + + // error message should be displayed by snap when try to watch an EOA with invalid input + await homePage.headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); + await accountListPage.addEoaAccount(input, message); }, ); }); @@ -216,30 +151,23 @@ describe('Account-watcher snap', function (this: Suite) { }) .withNetworkControllerOnMainnet() .build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { // watch an EOA address for ACCOUNT_2 - await watchEoaAddress(driver, true, ACCOUNT_2); - - // try to import private key of watched ACCOUNT_2 address - await driver.clickElement('[data-testid="account-menu-icon"]'); - await driver.clickElement( - '[data-testid="multichain-account-menu-popover-action-button"]', - ); - await driver.clickElement({ text: 'Import account', tag: 'button' }); - await driver.findClickableElement('#private-key-box'); - await driver.fill('#private-key-box', PRIVATE_KEY_TWO); - await driver.clickElement( - '[data-testid="import-account-confirm-button"]', + await loginWithBalanceValidation(driver); + await watchEoaAddress(driver, ACCOUNT_2); + const headerNavbar = new HeaderNavbar(driver); + await headerNavbar.check_accountLabel(DEFAULT_WATCHED_ACCOUNT_NAME); + + // try to import private key of watched ACCOUNT_2 address and check error message + await headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); + await accountListPage.addNewImportedAccount( + PRIVATE_KEY_TWO, + 'KeyringController - The account you are trying to import is a duplicate', ); - - // error message should be displayed - await driver.findElement({ - css: '.mm-box--color-error-default', - text: 'KeyringController - The account you are trying to import is a duplicate', - }); }, ); }); @@ -253,62 +181,27 @@ describe('Account-watcher snap', function (this: Suite) { }) .withNetworkControllerOnMainnet() .build(), - ganacheOptions: defaultGanacheOptions, - title: this.test?.fullTitle(), - }, - async ({ driver }: { driver: Driver }) => { - // watch an EOA address - await watchEoaAddress(driver); - - // click to view account details - await driver.clickElement( - '[data-testid="account-options-menu-button"]', - ); - await driver.clickElement( - '[data-testid="account-list-menu-details"]', - ); - // 'Show private key' button should not be displayed - await driver.assertElementNotPresent({ - css: 'button', - text: 'Show private key', - }); - }, - ); - }); - - it('removes a watched account', async function () { - await withFixtures( - { - fixtures: new FixtureBuilder() - .withPreferencesControllerAndFeatureFlag({ - watchEthereumAccountEnabled: true, - }) - .withNetworkControllerOnMainnet() - .build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { // watch an EOA address - await watchEoaAddress(driver); - - // remove the selected watched account - await removeSelectedAccount(driver); - - // account should be removed from the account list - await driver.assertElementNotPresent({ - css: '[data-testid="account-menu-icon"]', - text: DEFAULT_WATCHED_ACCOUNT_NAME, - }); - await driver.assertElementNotPresent({ - css: '.mm-text--ellipsis', - text: SHORTENED_EOA_ADDRESS, - }); + await loginWithBalanceValidation(driver); + await watchEoaAddress(driver, EOA_ADDRESS); + + // open account details modal in header navbar + const headerNavbar = new HeaderNavbar(driver); + await headerNavbar.check_accountLabel(DEFAULT_WATCHED_ACCOUNT_NAME); + await headerNavbar.openAccountDetailsModal(); + + // check 'Show private key' button should not be displayed + const accountDetailsModal = new AccountDetailsModal(driver); + await accountDetailsModal.check_pageIsLoaded(); + await accountDetailsModal.check_showPrivateKeyButtonIsNotDisplayed(); }, ); }); - it('can remove and recreate a watched account', async function () { + it('removes a watched account and recreate a watched account', async function () { await withFixtures( { fixtures: new FixtureBuilder() @@ -317,113 +210,83 @@ describe('Account-watcher snap', function (this: Suite) { }) .withNetworkControllerOnMainnet() .build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { // watch an EOA address - await watchEoaAddress(driver); + await loginWithBalanceValidation(driver); + await watchEoaAddress(driver, EOA_ADDRESS); + const homePage = new HomePage(driver); + await homePage.headerNavbar.check_accountLabel( + DEFAULT_WATCHED_ACCOUNT_NAME, + ); // remove the selected watched account - await removeSelectedAccount(driver); + await homePage.headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.removeAccount(DEFAULT_WATCHED_ACCOUNT_NAME); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); // account should be removed from the account list - await driver.assertElementNotPresent({ - css: '[data-testid="account-menu-icon"]', - text: DEFAULT_WATCHED_ACCOUNT_NAME, - }); - await driver.assertElementNotPresent({ - css: '.mm-text--ellipsis', - text: SHORTENED_EOA_ADDRESS, - }); - - // watch the same EOA address again - await watchEoaAddress(driver, false); - - // same account should be displayed in the account list - await driver.findElement({ - css: '[data-testid="account-menu-icon"]', - text: DEFAULT_WATCHED_ACCOUNT_NAME, - }); - await driver.findElement({ - css: '.mm-text--ellipsis', - text: SHORTENED_EOA_ADDRESS, - }); + await homePage.headerNavbar.openAccountMenu(); + await accountListPage.check_accountIsNotDisplayedInAccountList( + DEFAULT_WATCHED_ACCOUNT_NAME, + ); + await accountListPage.closeAccountModal(); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + + // watch the same EOA address again and check the account is recreated + await watchEoaAddress(driver, EOA_ADDRESS); + await homePage.headerNavbar.check_accountLabel( + DEFAULT_WATCHED_ACCOUNT_NAME, + ); + await homePage.headerNavbar.check_accountAddress( + SHORTENED_EOA_ADDRESS, + ); }, ); }); }); describe('Experimental toggle', function () { - const navigateToExperimentalSettings = async (driver: Driver) => { - await driver.clickElement('[data-testid="account-options-menu-button"]'); - await driver.clickElement({ text: 'Settings', tag: 'div' }); - await driver.clickElement({ text: 'Experimental', tag: 'div' }); - await driver.waitForSelector({ - text: messages.watchEthereumAccountsToggle.message, - tag: 'span', - }); - }; - - const getToggleState = async (driver: Driver): Promise => { - const toggleInput = await driver.findElement( - '[data-testid="watch-account-toggle"]', - ); - return toggleInput.isSelected(); - }; - - const toggleWatchAccountOptionAndCloseSettings = async (driver: Driver) => { - await driver.clickElement('[data-testid="watch-account-toggle-div"]'); - await driver.clickElement( - '.settings-page__header__title-container__close-button', - ); - }; - - const verifyWatchAccountOptionAndCloseMenu = async ( - driver: Driver, - shouldBePresent: boolean, - ) => { - await driver.clickElement('[data-testid="account-menu-icon"]'); - await driver.clickElement( - '[data-testid="multichain-account-menu-popover-action-button"]', - ); - if (shouldBePresent) { - await driver.waitForSelector({ - text: messages.addEthereumWatchOnlyAccount.message, - tag: 'button', - }); - } else { - await driver.assertElementNotPresent({ - text: messages.addEthereumWatchOnlyAccount.message, - tag: 'button', - }); - } - await driver.clickElement('header button[aria-label="Close"]'); - }; - it("will show the 'Watch an Ethereum account (Beta)' option when setting is enabled", async function () { await withFixtures( { fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }) => { - await unlockWallet(driver); - await navigateToExperimentalSettings(driver); - - // verify toggle is off by default + await loginWithBalanceValidation(driver); + const homePage = new HomePage(driver); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + + // navigate to experimental settings + await homePage.headerNavbar.openSettingsPage(); + const settingsPage = new SettingsPage(driver); + await settingsPage.check_pageIsLoaded(); + await settingsPage.goToExperimentalSettings(); + const experimentalSettings = new ExperimentalSettings(driver); + await experimentalSettings.check_pageIsLoaded(); + + // verify watch account toggle is off by default and enable the toggle assert.equal( - await getToggleState(driver), + await experimentalSettings.getWatchAccountToggleState(), false, 'Toggle should be off by default', ); - - // enable the toggle - await toggleWatchAccountOptionAndCloseSettings(driver); + await experimentalSettings.toggleWatchAccount(); + await settingsPage.closeSettingsPage(); // verify the 'Watch and Ethereum account (Beta)' option is available - await verifyWatchAccountOptionAndCloseMenu(driver, true); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + await homePage.headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); + await accountListPage.check_addWatchAccountAvailable(true); }, ); }); @@ -432,27 +295,49 @@ describe('Account-watcher snap', function (this: Suite) { await withFixtures( { fixtures: new FixtureBuilder().build(), - ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), }, async ({ driver }) => { - await unlockWallet(driver); - await navigateToExperimentalSettings(driver); - - // enable the toggle - await toggleWatchAccountOptionAndCloseSettings(driver); + await loginWithBalanceValidation(driver); + const homePage = new HomePage(driver); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + + // navigate to experimental settings and enable the toggle + await homePage.headerNavbar.openSettingsPage(); + const settingsPage = new SettingsPage(driver); + await settingsPage.check_pageIsLoaded(); + await settingsPage.goToExperimentalSettings(); + const experimentalSettings = new ExperimentalSettings(driver); + await experimentalSettings.check_pageIsLoaded(); + await experimentalSettings.toggleWatchAccount(); + await settingsPage.closeSettingsPage(); // verify the 'Watch and Ethereum account (Beta)' option is available - await verifyWatchAccountOptionAndCloseMenu(driver, true); - - // navigate back to experimental settings - await navigateToExperimentalSettings(driver); - - // disable the toggle - await toggleWatchAccountOptionAndCloseSettings(driver); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + await homePage.headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); + await accountListPage.check_addWatchAccountAvailable(true); + await accountListPage.closeAccountModal(); + + // navigate back to experimental settings and disable the toggle + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + await homePage.headerNavbar.openSettingsPage(); + await settingsPage.check_pageIsLoaded(); + await settingsPage.goToExperimentalSettings(); + await experimentalSettings.check_pageIsLoaded(); + await experimentalSettings.toggleWatchAccount(); + await settingsPage.closeSettingsPage(); // verify the 'Watch and Ethereum account (Beta)' option is not available - await verifyWatchAccountOptionAndCloseMenu(driver, false); + await homePage.check_pageIsLoaded(); + await homePage.check_expectedBalanceIsDisplayed(); + await homePage.headerNavbar.openAccountMenu(); + await accountListPage.check_pageIsLoaded(); + await accountListPage.check_addWatchAccountAvailable(false); }, ); }); diff --git a/test/e2e/flask/solana/common-solana.ts b/test/e2e/flask/solana/common-solana.ts index 2ac107bb442c..ac36d5ebed86 100644 --- a/test/e2e/flask/solana/common-solana.ts +++ b/test/e2e/flask/solana/common-solana.ts @@ -4,7 +4,7 @@ import { Driver } from '../../webdriver/driver'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; import AccountListPage from '../../page-objects/pages/account-list-page'; import FixtureBuilder from '../../fixture-builder'; -import { ACCOUNT_TYPE } from '../../page-objects/common'; +import { ACCOUNT_TYPE } from '../../constants'; import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; const SOLANA_URL_REGEX = /^https:\/\/.*\.solana.*/u; @@ -64,7 +64,10 @@ export async function withSolanaAccountSnap( const headerComponen = new HeaderNavbar(driver); await headerComponen.openAccountMenu(); const accountListPage = new AccountListPage(driver); - await accountListPage.addAccount(ACCOUNT_TYPE.Solana, 'Solana 1'); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Solana, + accountName: 'Solana 1', + }); await test(driver, mockServer); }, ); diff --git a/test/e2e/flask/solana/create-solana-account.spec.ts b/test/e2e/flask/solana/create-solana-account.spec.ts index cca4f5222993..0cac9fab7375 100644 --- a/test/e2e/flask/solana/create-solana-account.spec.ts +++ b/test/e2e/flask/solana/create-solana-account.spec.ts @@ -1,7 +1,7 @@ import { Suite } from 'mocha'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; import AccountListPage from '../../page-objects/pages/account-list-page'; -import { ACCOUNT_TYPE } from '../../page-objects/common'; +import { ACCOUNT_TYPE } from '../../constants'; import { withSolanaAccountSnap } from './common-solana'; // Scenarios skipped due to https://consensyssoftware.atlassian.net/browse/SOL-87 @@ -17,7 +17,10 @@ describe('Create Solana Account', function (this: Suite) { await headerNavbar.openAccountMenu(); const accountListPage = new AccountListPage(driver); await accountListPage.check_accountDisplayedInAccountList('Account 1'); - await accountListPage.addAccount(ACCOUNT_TYPE.Solana, 'Solana 2'); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Solana, + accountName: 'Solana 2', + }); await headerNavbar.check_accountLabel('Solana 2'); await headerNavbar.openAccountMenu(); await accountListPage.check_numberOfAvailableAccounts(3); diff --git a/test/e2e/page-objects/common.ts b/test/e2e/page-objects/common.ts index 40eb625d94ac..5bf1a91e1859 100644 --- a/test/e2e/page-objects/common.ts +++ b/test/e2e/page-objects/common.ts @@ -2,9 +2,3 @@ export type RawLocator = | string | { css?: string; text?: string } | { tag: string; text: string }; - -export enum ACCOUNT_TYPE { - Ethereum, - Bitcoin, - Solana, -} diff --git a/test/e2e/page-objects/flows/watch-account.flow.ts b/test/e2e/page-objects/flows/watch-account.flow.ts new file mode 100644 index 000000000000..8481c71599a6 --- /dev/null +++ b/test/e2e/page-objects/flows/watch-account.flow.ts @@ -0,0 +1,22 @@ +import { Driver } from '../../webdriver/driver'; +import HomePage from '../pages/home/homepage'; +import AccountListPage from '../pages/account-list-page'; + +/** + * Initiates the flow of watching an EOA address. + * + * @param driver - The WebDriver instance. + * @param address - The EOA address that is to be watched. + */ +export async function watchEoaAddress( + driver: Driver, + address: string, +): Promise { + // watch a new EOA + const homePage = new HomePage(driver); + await homePage.headerNavbar.openAccountMenu(); + const accountListPage = new AccountListPage(driver); + await accountListPage.check_pageIsLoaded(); + await accountListPage.addEoaAccount(address); + await homePage.check_pageIsLoaded(); +} diff --git a/test/e2e/page-objects/pages/account-list-page.ts b/test/e2e/page-objects/pages/account-list-page.ts index 628d758e7e71..92eede81652b 100644 --- a/test/e2e/page-objects/pages/account-list-page.ts +++ b/test/e2e/page-objects/pages/account-list-page.ts @@ -1,13 +1,11 @@ import { Driver } from '../../webdriver/driver'; import { largeDelayMs, regularDelayMs } from '../../helpers'; import messages from '../../../../app/_locales/en/messages.json'; -import { ACCOUNT_TYPE } from '../common'; +import { ACCOUNT_TYPE } from '../../constants'; class AccountListPage { private readonly driver: Driver; - private readonly accountAddressText = '.qr-code__address-segments'; - private readonly accountListAddressItem = '[data-testid="account-list-address"]'; @@ -28,10 +26,6 @@ class AccountListPage { private readonly accountOptionsMenuButton = '[data-testid="account-list-item-menu-button"]'; - private readonly accountQrCodeImage = '.qr-code__wrapper'; - - private readonly accountQrCodeAddress = '.qr-code__address-segments'; - private readonly addAccountConfirmButton = '[data-testid="submit-add-account-with-name"]'; @@ -48,6 +42,9 @@ class AccountListPage { private readonly addEthereumAccountButton = '[data-testid="multichain-account-menu-popover-add-account"]'; + private readonly addEoaAccountButton = + '[data-testid="multichain-account-menu-popover-add-watch-only-account"]'; + private readonly addHardwareWalletButton = { text: 'Add hardware wallet', tag: 'button', @@ -70,11 +67,6 @@ class AccountListPage { private readonly currentSelectedAccount = '.multichain-account-list-item--selected'; - private readonly editableLabelButton = - '[data-testid="editable-label-button"]'; - - private readonly editableLabelInput = '[data-testid="editable-input"] input'; - private readonly hiddenAccountOptionsMenuButton = '.multichain-account-menu-popover__list--menu-item-hidden-account [data-testid="account-list-item-menu-button"]'; @@ -124,8 +116,18 @@ class AccountListPage { tag: 'button', }; - private readonly saveAccountLabelButton = - '[data-testid="save-account-label-input"]'; + private readonly watchAccountAddressInput = + 'input#address-input[type="text"]'; + + private readonly watchAccountConfirmButton = { + text: 'Watch account', + tag: 'button', + }; + + private readonly watchAccountModalTitle = { + text: 'Watch any Ethereum account', + tag: 'h4', + }; constructor(driver: Driver) { this.driver = driver; @@ -145,34 +147,36 @@ class AccountListPage { } /** - * Adds a new account with an optional custom label. + * Watch an EOA (external owned account). * - * @param customLabel - The custom label for the new account. If not provided, a default name will be used. + * @param address - The address to watch. + * @param expectedErrorMessage - Optional error message to display if the address is invalid. */ - async addNewAccount(customLabel?: string): Promise { - if (customLabel) { - console.log(`Adding new account with custom label: ${customLabel}`); - } else { - console.log(`Adding new account with default name`); - } + async addEoaAccount( + address: string, + expectedErrorMessage: string = '', + ): Promise { + console.log(`Watch EOA account with address ${address}`); await this.driver.clickElement(this.createAccountButton); - await this.driver.clickElement(this.addEthereumAccountButton); - if (customLabel) { - await this.driver.fill(this.accountNameInput, customLabel); - } - // needed to mitigate a race condition with the state update - // there is no condition we can wait for in the UI - await this.driver.delay(largeDelayMs); + await this.driver.clickElement(this.addEoaAccountButton); + await this.driver.waitForSelector(this.watchAccountModalTitle); + await this.driver.fill(this.watchAccountAddressInput, address); await this.driver.clickElementAndWaitToDisappear( - this.addAccountConfirmButton, + this.watchAccountConfirmButton, ); - } - - async isBtcAccountCreationButtonEnabled() { - const createButton = await this.driver.findElement( - this.addBtcAccountButton, - ); - return await createButton.isEnabled(); + if (expectedErrorMessage) { + console.log( + `Check if error message is displayed: ${expectedErrorMessage}`, + ); + await this.driver.waitForSelector({ + css: '.snap-ui-renderer__text', + text: expectedErrorMessage, + }); + } else { + await this.driver.clickElementAndWaitToDisappear( + this.addAccountConfirmButton, + ); + } } /** @@ -205,20 +209,27 @@ class AccountListPage { /** * Adds a new account of the specified type with an optional custom name. * - * @param accountType - The type of account to add (Ethereum, Bitcoin, or Solana) - * @param accountName - Optional custom name for the new account + * @param options - Options for adding a new account + * @param options.accountType - The type of account to add (Ethereum, Bitcoin, or Solana) + * @param [options.accountName] - Optional custom name for the new account * @throws {Error} If the specified account type is not supported * @example * // Add a new Ethereum account with default name - * await accountListPage.addAccount(ACCOUNT_TYPE.Ethereum); + * await accountListPage.addAccount({ accountType: ACCOUNT_TYPE.Ethereum }); * * // Add a new Bitcoin account with custom name - * await accountListPage.addAccount(ACCOUNT_TYPE.Bitcoin, 'My BTC Wallet'); + * await accountListPage.addAccount({ accountType: ACCOUNT_TYPE.Bitcoin, accountName: 'My BTC Wallet' }); */ - async addAccount(accountType: ACCOUNT_TYPE, accountName?: string) { + async addAccount({ + accountType, + accountName, + }: { + accountType: ACCOUNT_TYPE; + accountName?: string; + }) { + console.log(`Adding new account of type: ${ACCOUNT_TYPE[accountType]}`); await this.driver.clickElement(this.createAccountButton); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let addAccountButton: any; + let addAccountButton; switch (accountType) { case ACCOUNT_TYPE.Ethereum: addAccountButton = this.addEthereumAccountButton; @@ -235,56 +246,20 @@ class AccountListPage { await this.driver.clickElement(addAccountButton); if (accountName) { + console.log( + `Customize the new account with account name: ${accountName}`, + ); await this.driver.fill(this.accountNameInput, accountName); } - + // needed to mitigate a race condition with the state update + // there is no condition we can wait for in the UI + await this.driver.delay(largeDelayMs); await this.driver.clickElementAndWaitToDisappear( this.addAccountConfirmButton, 5000, ); } - /** - * Changes the label of the current account. - * - * @param newLabel - The new label to set for the account. - */ - async changeAccountLabel(newLabel: string): Promise { - console.log(`Changing account label to: ${newLabel}`); - await this.driver.clickElement(this.accountMenuButton); - await this.changeLabelFromAccountDetailsModal(newLabel); - } - - /** - * Changes the account label from within an already opened account details modal. - * Note: This method assumes the account details modal is already open. - * - * Recommended usage: - * ```typescript - * await accountListPage.openAccountDetailsModal('Current Account Name'); - * await accountListPage.changeLabelFromAccountDetailsModal('New Account Name'); - * ``` - * - * @param newLabel - The new label to set for the account - * @throws Will throw an error if the modal is not open when method is called - * @example - * // To rename a specific account, first open its details modal: - * await accountListPage.openAccountDetailsModal('Current Account Name'); - * await accountListPage.changeLabelFromAccountDetailsModal('New Account Name'); - * - * // Note: Using changeAccountLabel() alone will only work for the first account - */ - async changeLabelFromAccountDetailsModal(newLabel: string): Promise { - await this.driver.waitForSelector(this.editableLabelButton); - console.log( - `Account details modal opened, changing account label to: ${newLabel}`, - ); - await this.driver.clickElement(this.editableLabelButton); - await this.driver.fill(this.editableLabelInput, newLabel); - await this.driver.clickElement(this.saveAccountLabelButton); - await this.driver.clickElement(this.closeAccountModalButton); - } - async closeAccountModal(): Promise { console.log(`Close account modal in account list`); await this.driver.clickElementAndWaitToDisappear( @@ -292,23 +267,6 @@ class AccountListPage { ); } - /** - * Get the address of the specified account. - * - * @param accountLabel - The label of the account to get the address. - */ - async getAccountAddress(accountLabel: string): Promise { - console.log(`Get account address in account list`); - await this.openAccountOptionsInAccountList(accountLabel); - await this.driver.clickElement(this.accountMenuButton); - await this.driver.waitForSelector(this.accountAddressText); - const accountAddress = await ( - await this.driver.findElement(this.accountAddressText) - ).getText(); - await this.driver.clickElement(this.closeAccountModalButton); - return accountAddress; - } - async hideAccount(): Promise { console.log(`Hide account in account list`); await this.driver.clickElement(this.hideUnhideAccountButton); @@ -340,6 +298,13 @@ class AccountListPage { ); } + async isBtcAccountCreationButtonEnabled(): Promise { + const createButton = await this.driver.findElement( + this.addBtcAccountButton, + ); + return await createButton.isEnabled(); + } + /** * Open the account details modal for the specified account in account list. * @@ -556,21 +521,24 @@ class AccountListPage { } /** - * Check that the correct address is displayed in the account details modal. + * Checks that the add watch account button is displayed in the create account modal. * - * @param expectedAddress - The expected address to check. + * @param expectedAvailability - Whether the add watch account button is expected to be displayed. */ - async check_addressInAccountDetailsModal( - expectedAddress: string, + async check_addWatchAccountAvailable( + expectedAvailability: boolean, ): Promise { console.log( - `Check that address ${expectedAddress} is displayed in account details modal`, + `Check add watch account button is ${ + expectedAvailability ? 'displayed ' : 'not displayed' + }`, ); - await this.driver.waitForSelector(this.accountQrCodeImage); - await this.driver.waitForSelector({ - css: this.accountQrCodeAddress, - text: expectedAddress, - }); + await this.openAddAccountModal(); + if (expectedAvailability) { + await this.driver.waitForSelector(this.addEoaAccountButton); + } else { + await this.driver.assertElementNotPresent(this.addEoaAccountButton); + } } /** diff --git a/test/e2e/page-objects/pages/dialog/account-details-modal.ts b/test/e2e/page-objects/pages/dialog/account-details-modal.ts new file mode 100644 index 000000000000..0dbb2e0f87d7 --- /dev/null +++ b/test/e2e/page-objects/pages/dialog/account-details-modal.ts @@ -0,0 +1,106 @@ +import { Driver } from '../../../webdriver/driver'; + +class AccountDetailsModal { + private driver: Driver; + + private readonly accountAddressText = '.qr-code__address-segments'; + + private readonly accountQrCodeAddress = '.qr-code__address-segments'; + + private readonly accountQrCodeImage = '.qr-code__wrapper'; + + private readonly closeAccountModalButton = + 'header button[aria-label="Close"]'; + + private readonly copyAddressButton = + '[data-testid="address-copy-button-text"]'; + + private readonly editableLabelButton = + '[data-testid="editable-label-button"]'; + + private readonly editableLabelInput = '[data-testid="editable-input"] input'; + + private readonly saveAccountLabelButton = + '[data-testid="save-account-label-input"]'; + + private readonly showPrivateKeyButton = { + css: 'button', + text: 'Show private key', + }; + + constructor(driver: Driver) { + this.driver = driver; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.editableLabelButton, + this.copyAddressButton, + ]); + } catch (e) { + console.log( + 'Timeout while waiting for account details modal to be loaded', + e, + ); + throw e; + } + console.log('Account details modal is loaded'); + } + + async closeAccountDetailsModal(): Promise { + await this.driver.clickElementAndWaitToDisappear( + this.closeAccountModalButton, + ); + } + + /** + * Change the label of the account in the account details modal. + * + * @param newLabel - The new label to set for the account. + */ + async changeAccountLabel(newLabel: string): Promise { + console.log( + `Account details modal opened, changing account label to: ${newLabel}`, + ); + await this.driver.clickElement(this.editableLabelButton); + await this.driver.fill(this.editableLabelInput, newLabel); + await this.driver.clickElement(this.saveAccountLabelButton); + await this.closeAccountDetailsModal(); + } + + async getAccountAddress(): Promise { + console.log(`Get account address in account details modal`); + await this.driver.waitForSelector(this.accountAddressText); + const accountAddress = await ( + await this.driver.findElement(this.accountAddressText) + ).getText(); + await this.closeAccountDetailsModal(); + return accountAddress; + } + + /** + * Check that the correct address is displayed in the account details modal. + * + * @param expectedAddress - The expected address to check. + */ + async check_addressInAccountDetailsModal( + expectedAddress: string, + ): Promise { + console.log( + `Check that address ${expectedAddress} is displayed in account details modal`, + ); + await this.driver.waitForSelector(this.accountQrCodeImage); + await this.driver.waitForSelector({ + css: this.accountQrCodeAddress, + text: expectedAddress, + }); + } + + async check_showPrivateKeyButtonIsNotDisplayed(): Promise { + console.log('Check that show private key button is not displayed'); + await this.driver.assertElementNotPresent(this.showPrivateKeyButton); + } +} + +export default AccountDetailsModal; diff --git a/test/e2e/page-objects/pages/header-navbar.ts b/test/e2e/page-objects/pages/header-navbar.ts index 57e02e864624..e64fdc5a4cd7 100644 --- a/test/e2e/page-objects/pages/header-navbar.ts +++ b/test/e2e/page-objects/pages/header-navbar.ts @@ -9,6 +9,8 @@ class HeaderNavbar { private readonly allPermissionsButton = '[data-testid="global-menu-connected-sites"]'; + private readonly copyAddressButton = '[data-testid="app-header-copy-button"]'; + private readonly threeDotMenuButton = '[data-testid="account-options-menu-button"]'; @@ -19,6 +21,9 @@ class HeaderNavbar { private readonly mmiPortfolioButton = '[data-testid="global-menu-mmi-portfolio"]'; + private readonly openAccountDetailsButton = + '[data-testid="account-list-menu-details"]'; + private readonly settingsButton = '[data-testid="global-menu-settings"]'; private readonly switchNetworkDropDown = '[data-testid="network-display"]'; @@ -51,6 +56,12 @@ class HeaderNavbar { await this.driver.clickElement(this.accountMenuButton); } + async openAccountDetailsModal(): Promise { + console.log('Open account details modal'); + await this.openThreeDotMenu(); + await this.driver.clickElement(this.openAccountDetailsButton); + } + async openThreeDotMenu(): Promise { console.log('Open account options menu'); await this.driver.clickElement(this.threeDotMenuButton); @@ -98,6 +109,21 @@ class HeaderNavbar { ); } + /** + * Verifies that the displayed account address in header matches the expected address. + * + * @param expectedAddress - The expected address of the account. + */ + async check_accountAddress(expectedAddress: string): Promise { + console.log( + `Verify the displayed account address in header is: ${expectedAddress}`, + ); + await this.driver.waitForSelector({ + css: this.copyAddressButton, + text: expectedAddress, + }); + } + /** * Verifies that the displayed account label in header matches the expected label. * diff --git a/test/e2e/page-objects/pages/home/bitcoin-homepage.ts b/test/e2e/page-objects/pages/home/bitcoin-homepage.ts index 19b235636405..691d85763b14 100644 --- a/test/e2e/page-objects/pages/home/bitcoin-homepage.ts +++ b/test/e2e/page-objects/pages/home/bitcoin-homepage.ts @@ -4,10 +4,7 @@ class BitcoinHomepage extends HomePage { protected readonly balance = '[data-testid="coin-overview__primary-currency"]'; - private readonly bridgeButton = { - text: 'Bridge', - tag: 'button', - }; + protected readonly bridgeButton = '[data-testid="coin-overview-bridge"]'; private readonly buySellButton = '[data-testid="coin-overview-buy"]'; @@ -15,10 +12,7 @@ class BitcoinHomepage extends HomePage { protected readonly sendButton = '[data-testid="coin-overview-send"]'; - private readonly swapButton = { - text: 'Swap', - tag: 'button', - }; + protected readonly swapButton = '[data-testid="coin-overview-swap"]'; async check_pageIsLoaded(): Promise { try { diff --git a/test/e2e/page-objects/pages/home/homepage.ts b/test/e2e/page-objects/pages/home/homepage.ts index b4b79846fb06..708ae5ac0277 100644 --- a/test/e2e/page-objects/pages/home/homepage.ts +++ b/test/e2e/page-objects/pages/home/homepage.ts @@ -20,6 +20,9 @@ class HomePage { css: '.mm-banner-alert', }; + protected readonly bridgeButton: string = + '[data-testid="eth-overview-bridge"]'; + private readonly closeUseNetworkNotificationModalButton = { text: 'Got it', tag: 'h6', @@ -45,12 +48,15 @@ class HomePage { testId: 'sensitive-toggle', }; + protected readonly sendButton: string = '[data-testid="eth-overview-send"]'; + + protected readonly swapButton: string = + '[data-testid="token-overview-button-swap"]'; + private readonly refreshErc20Tokens = { testId: 'refreshList', }; - protected readonly sendButton: string = '[data-testid="eth-overview-send"]'; - private readonly tokensTab = { testId: 'account-overview__asset-tab', }; @@ -142,6 +148,13 @@ class HomePage { await this.driver.waitForSelector(this.basicFunctionalityOffWarningMessage); } + async check_disabledButtonTooltip(tooltipText: string): Promise { + console.log(`Check if disabled button tooltip is displayed on homepage`); + await this.driver.waitForSelector( + `.icon-button--disabled [data-tooltipped][data-original-title="${tooltipText}"]`, + ); + } + /** * Checks if the toaster message for editing a network is displayed on the homepage. * @@ -197,6 +210,39 @@ class HomePage { }, 10000); } + async check_ifBridgeButtonIsClickable(): Promise { + try { + await this.driver.findClickableElement(this.bridgeButton, 1000); + } catch (e) { + console.log('Bridge button not clickable', e); + return false; + } + console.log('Bridge button is clickable'); + return true; + } + + async check_ifSendButtonIsClickable(): Promise { + try { + await this.driver.findClickableElement(this.sendButton, 1000); + } catch (e) { + console.log('Send button not clickable', e); + return false; + } + console.log('Send button is clickable'); + return true; + } + + async check_ifSwapButtonIsClickable(): Promise { + try { + await this.driver.findClickableElement(this.swapButton, 1000); + } catch (e) { + console.log('Swap button not clickable', e); + return false; + } + console.log('Swap button is clickable'); + return true; + } + async check_localBlockchainBalanceIsDisplayed( localBlockchainServer?: Ganache, address = null, diff --git a/test/e2e/page-objects/pages/settings/experimental-settings.ts b/test/e2e/page-objects/pages/settings/experimental-settings.ts index f551db6d47c5..69df0525093d 100644 --- a/test/e2e/page-objects/pages/settings/experimental-settings.ts +++ b/test/e2e/page-objects/pages/settings/experimental-settings.ts @@ -22,6 +22,12 @@ class ExperimentalSettings { private readonly requestQueueToggle = '[data-testid="experimental-setting-toggle-request-queue"] label'; + private readonly watchAccountToggleState = + '[data-testid="watch-account-toggle"]'; + + private readonly watchAccountToggle = + '[data-testid="watch-account-toggle-div"]'; + constructor(driver: Driver) { this.driver = driver; } @@ -39,6 +45,15 @@ class ExperimentalSettings { console.log('Experimental Settings page is loaded'); } + // Get the state of the Watch Account Toggle, returns true if the toggle is selected + async getWatchAccountToggleState(): Promise { + console.log('Get Watch Account Toggle State'); + const toggleInput = await this.driver.findElement( + this.watchAccountToggleState, + ); + return toggleInput.isSelected(); + } + async toggleBitcoinAccount(): Promise { console.log('Toggle Add new Bitcoin account on experimental setting page'); await this.driver.waitForSelector({ @@ -62,6 +77,11 @@ class ExperimentalSettings { console.log('Toggle Request Queue on experimental setting page'); await this.driver.clickElement(this.requestQueueToggle); } + + async toggleWatchAccount(): Promise { + console.log('Toggle Watch Account on experimental setting page'); + await this.driver.clickElement(this.watchAccountToggle); + } } export default ExperimentalSettings; diff --git a/test/e2e/tests/account/account-custom-name.spec.ts b/test/e2e/tests/account/account-custom-name.spec.ts index 4c0ecbe196f9..92302e032224 100644 --- a/test/e2e/tests/account/account-custom-name.spec.ts +++ b/test/e2e/tests/account/account-custom-name.spec.ts @@ -1,8 +1,10 @@ import { Suite } from 'mocha'; import { Driver } from '../../webdriver/driver'; import { withFixtures } from '../../helpers'; +import { ACCOUNT_TYPE } from '../../constants'; import FixtureBuilder from '../../fixture-builder'; import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; +import AccountDetailsModal from '../../page-objects/pages/dialog/account-details-modal'; import AccountListPage from '../../page-objects/pages/account-list-page'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; @@ -25,14 +27,20 @@ describe('Account Custom Name Persistence', function (this: Suite) { // Change account label for existing account and verify edited account label const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.openAccountOptionsMenu(); - await accountListPage.changeAccountLabel(newAccountLabel); + await accountListPage.openAccountDetailsModal('Account 1'); + + const accountDetailsModal = new AccountDetailsModal(driver); + await accountDetailsModal.check_pageIsLoaded(); + await accountDetailsModal.changeAccountLabel(newAccountLabel); await headerNavbar.check_accountLabel(newAccountLabel); // Add new account with custom label and verify new added account label await headerNavbar.openAccountMenu(); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewAccount(anotherAccountLabel); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + accountName: anotherAccountLabel, + }); await headerNavbar.check_accountLabel(anotherAccountLabel); // Switch back to the first account and verify first custom account persists diff --git a/test/e2e/tests/account/add-account.spec.ts b/test/e2e/tests/account/add-account.spec.ts index db62927b91d7..2df140899212 100644 --- a/test/e2e/tests/account/add-account.spec.ts +++ b/test/e2e/tests/account/add-account.spec.ts @@ -1,5 +1,6 @@ import { E2E_SRP } from '../../default-fixture'; import FixtureBuilder from '../../fixture-builder'; +import { ACCOUNT_TYPE } from '../../constants'; import { WALLET_PASSWORD, defaultGanacheOptions, @@ -43,7 +44,9 @@ describe('Add account', function () { const newAccountName = 'Account 2'; const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewAccount(); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + }); await headerNavbar.check_accountLabel(newAccountName); await homePage.check_expectedBalanceIsDisplayed(); @@ -112,7 +115,9 @@ describe('Add account', function () { const newAccountName = 'Account 2'; const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewAccount(); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + }); await headerNavbar.check_accountLabel(newAccountName); await homePage.check_expectedBalanceIsDisplayed(); @@ -177,7 +182,9 @@ describe('Add account', function () { // Create new account with default name Account 2 const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewAccount(); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + }); await headerNavbar.check_accountLabel('Account 2'); await homePage.check_expectedBalanceIsDisplayed(); diff --git a/test/e2e/tests/account/import-flow.spec.ts b/test/e2e/tests/account/import-flow.spec.ts index dbb7b2f85d49..c8ffcc334e49 100644 --- a/test/e2e/tests/account/import-flow.spec.ts +++ b/test/e2e/tests/account/import-flow.spec.ts @@ -3,6 +3,7 @@ import { DEFAULT_FIXTURE_ACCOUNT } from '../../constants'; import { withFixtures } from '../../helpers'; import FixtureBuilder from '../../fixture-builder'; import AccountListPage from '../../page-objects/pages/account-list-page'; +import AccountDetailsModal from '../../page-objects/pages/dialog/account-details-modal'; import HeaderNavbar from '../../page-objects/pages/header-navbar'; import HomePage from '../../page-objects/pages/home/homepage'; import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'; @@ -32,7 +33,9 @@ describe('Import flow @no-mmi', function () { const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); await accountListPage.openAccountDetailsModal('Account 1'); - await accountListPage.check_addressInAccountDetailsModal( + const accountDetailsModal = new AccountDetailsModal(driver); + await accountDetailsModal.check_pageIsLoaded(); + await accountDetailsModal.check_addressInAccountDetailsModal( DEFAULT_FIXTURE_ACCOUNT.toLowerCase(), ); }, diff --git a/test/e2e/tests/identity/account-syncing/new-user-sync.spec.ts b/test/e2e/tests/identity/account-syncing/new-user-sync.spec.ts index e9a82b128352..64a3ddbf5fd7 100644 --- a/test/e2e/tests/identity/account-syncing/new-user-sync.spec.ts +++ b/test/e2e/tests/identity/account-syncing/new-user-sync.spec.ts @@ -2,6 +2,7 @@ import { Mockttp } from 'mockttp'; import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; +import { ACCOUNT_TYPE } from '../../../constants'; import { mockIdentityServices } from '../mocks'; import { IDENTITY_TEAM_PASSWORD } from '../constants'; import { UserStorageMockttpController } from '../../../helpers/identity/user-storage/userStorageMockttpController'; @@ -64,7 +65,10 @@ describe('Account syncing - New User @no-mmi', function () { // Add a second account await accountListPage.openAccountOptionsMenu(); - await accountListPage.addNewAccount('My Second Account'); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + accountName: 'My Second Account', + }); // Set SRP to use for retreival const headerNavbar = new HeaderNavbar(driver); diff --git a/test/e2e/tests/identity/account-syncing/onboarding-with-opt-out.spec.ts b/test/e2e/tests/identity/account-syncing/onboarding-with-opt-out.spec.ts index 19908211181b..bd063c182baf 100644 --- a/test/e2e/tests/identity/account-syncing/onboarding-with-opt-out.spec.ts +++ b/test/e2e/tests/identity/account-syncing/onboarding-with-opt-out.spec.ts @@ -7,6 +7,7 @@ import { IDENTITY_TEAM_PASSWORD, IDENTITY_TEAM_SEED_PHRASE, } from '../constants'; +import { ACCOUNT_TYPE } from '../../../constants'; import { UserStorageMockttpController } from '../../../helpers/identity/user-storage/userStorageMockttpController'; import AccountListPage from '../../../page-objects/pages/account-list-page'; import HeaderNavbar from '../../../page-objects/pages/header-navbar'; @@ -135,7 +136,11 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { await accountListPage.check_accountDisplayedInAccountList( 'Account 1', ); - await accountListPage.addNewAccount('New Account'); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + accountName: 'New Account', + }); + // Set SRP to use for retreival const headerNavbar = new HeaderNavbar(driver); await headerNavbar.check_pageIsLoaded(); diff --git a/test/e2e/tests/identity/account-syncing/sync-after-adding-account.spec.ts b/test/e2e/tests/identity/account-syncing/sync-after-adding-account.spec.ts index 9c2fdb69c7e9..e02833e7c172 100644 --- a/test/e2e/tests/identity/account-syncing/sync-after-adding-account.spec.ts +++ b/test/e2e/tests/identity/account-syncing/sync-after-adding-account.spec.ts @@ -3,6 +3,7 @@ import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sd import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; import { mockIdentityServices } from '../mocks'; +import { ACCOUNT_TYPE } from '../../../constants'; import { IDENTITY_TEAM_PASSWORD, IDENTITY_TEAM_SEED_PHRASE, @@ -65,7 +66,10 @@ describe('Account syncing - Add Account @no-mmi', function () { await accountListPage.check_accountDisplayedInAccountList( 'My Second Synced Account', ); - await accountListPage.addNewAccount('My third account'); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + accountName: 'My third account', + }); }, ); @@ -164,7 +168,9 @@ describe('Account syncing - Add Account @no-mmi', function () { await accountListPage.check_accountDisplayedInAccountList( 'My Second Synced Account', ); - await accountListPage.addNewAccount(); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + }); }, ); diff --git a/test/e2e/tests/identity/account-syncing/sync-after-modifying-account-name.spec.ts b/test/e2e/tests/identity/account-syncing/sync-after-modifying-account-name.spec.ts index 07f6e4848aba..ce10b3129d58 100644 --- a/test/e2e/tests/identity/account-syncing/sync-after-modifying-account-name.spec.ts +++ b/test/e2e/tests/identity/account-syncing/sync-after-modifying-account-name.spec.ts @@ -9,6 +9,7 @@ import { } from '../constants'; import { UserStorageMockttpController } from '../../../helpers/identity/user-storage/userStorageMockttpController'; import HeaderNavbar from '../../../page-objects/pages/header-navbar'; +import AccountDetailsModal from '../../../page-objects/pages/dialog/account-details-modal'; import AccountListPage from '../../../page-objects/pages/account-list-page'; import HomePage from '../../../page-objects/pages/home/homepage'; import { completeImportSRPOnboardingFlow } from '../../../page-objects/flows/onboarding.flow'; @@ -65,8 +66,14 @@ describe('Account syncing - Rename Accounts @no-mmi', function () { await accountListPage.check_accountDisplayedInAccountList( 'My Second Synced Account', ); - await accountListPage.openAccountOptionsMenu(); - await accountListPage.changeAccountLabel('My Renamed First Account'); + await accountListPage.openAccountDetailsModal( + 'My First Synced Account', + ); + const accountDetailsModal = new AccountDetailsModal(driver); + await accountDetailsModal.check_pageIsLoaded(); + await accountDetailsModal.changeAccountLabel( + 'My Renamed First Account', + ); }, ); diff --git a/test/e2e/tests/identity/account-syncing/sync-with-account-balances.spec.ts b/test/e2e/tests/identity/account-syncing/sync-with-account-balances.spec.ts index 6fcc9e9cc0d0..ec52bb3124bd 100644 --- a/test/e2e/tests/identity/account-syncing/sync-with-account-balances.spec.ts +++ b/test/e2e/tests/identity/account-syncing/sync-with-account-balances.spec.ts @@ -6,8 +6,10 @@ import { IDENTITY_TEAM_PASSWORD, IDENTITY_TEAM_SEED_PHRASE, } from '../constants'; +import { ACCOUNT_TYPE } from '../../../constants'; import { UserStorageMockttpController } from '../../../helpers/identity/user-storage/userStorageMockttpController'; import HeaderNavbar from '../../../page-objects/pages/header-navbar'; +import AccountDetailsModal from '../../../page-objects/pages/dialog/account-details-modal'; import AccountListPage from '../../../page-objects/pages/account-list-page'; import HomePage from '../../../page-objects/pages/home/homepage'; import { completeImportSRPOnboardingFlow } from '../../../page-objects/flows/onboarding.flow'; @@ -108,7 +110,9 @@ describe('Account syncing - User already has balances on multple accounts @no-mm } // Create new account and prepare for additional accounts - await accountListPage.addNewAccount(); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + }); accountsToMock = [...INITIAL_ACCOUNTS, ...ADDITIONAL_ACCOUNTS]; }, ); @@ -158,11 +162,13 @@ describe('Account syncing - User already has balances on multple accounts @no-mm // Rename Account 6 to verify update to user storage await accountListPage.switchToAccount('Account 6'); + await header.check_accountLabel('Account 6'); await header.openAccountMenu(); + await accountListPage.check_pageIsLoaded(); await accountListPage.openAccountDetailsModal('Account 6'); - await accountListPage.changeLabelFromAccountDetailsModal( - 'My Renamed Account 6', - ); + const accountDetailsModal = new AccountDetailsModal(driver); + await accountDetailsModal.check_pageIsLoaded(); + await accountDetailsModal.changeAccountLabel('My Renamed Account 6'); }, ); diff --git a/test/e2e/tests/tokens/nft/import-nft.spec.ts b/test/e2e/tests/tokens/nft/import-nft.spec.ts index 5983d1002035..e151e83a355f 100644 --- a/test/e2e/tests/tokens/nft/import-nft.spec.ts +++ b/test/e2e/tests/tokens/nft/import-nft.spec.ts @@ -1,4 +1,5 @@ import { defaultGanacheOptions, withFixtures } from '../../../helpers'; +import { ACCOUNT_TYPE } from '../../../constants'; import { SMART_CONTRACTS } from '../../../seeder/smart-contracts'; import FixtureBuilder from '../../../fixture-builder'; import AccountListPage from '../../../page-objects/pages/account-list-page'; @@ -67,7 +68,9 @@ describe('Import NFT', function () { await headerNavbar.openAccountMenu(); const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - await accountListPage.addNewAccount(); + await accountListPage.addAccount({ + accountType: ACCOUNT_TYPE.Ethereum, + }); await headerNavbar.check_accountLabel('Account 2'); await homepage.check_expectedBalanceIsDisplayed(); From 7be1b0d3b193764eef0489ba3c9df49eba01f2fa Mon Sep 17 00:00:00 2001 From: Priya Date: Fri, 20 Dec 2024 11:55:45 +0100 Subject: [PATCH 3/9] test: remove duplicate signature tests (#29377) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Removing duplicated tests that are already covered in test/e2e/tests/confirmations/signatures [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29377?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **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/main/.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/main/.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. --- .../e2e/tests/signature/personal-sign.spec.js | 37 ------------ .../tests/signature/signature-request.spec.js | 58 ------------------- 2 files changed, 95 deletions(-) diff --git a/test/e2e/tests/signature/personal-sign.spec.js b/test/e2e/tests/signature/personal-sign.spec.js index 092a9518ba01..908a422bfea0 100644 --- a/test/e2e/tests/signature/personal-sign.spec.js +++ b/test/e2e/tests/signature/personal-sign.spec.js @@ -116,43 +116,6 @@ describe('Personal sign', function () { }); describe('Redesigned confirmation screens', function () { - it('can initiate and confirm a personal sign', async function () { - await withFixtures( - { - dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() - .build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver, ganacheServer }) => { - const addresses = await ganacheServer.getAccounts(); - const publicAddress = addresses[0]; - await unlockWallet(driver); - - await openDapp(driver); - await driver.clickElement('#personalSign'); - - await driver.waitUntilXWindowHandles(3); - const windowHandles = await driver.getAllWindowHandles(); - await driver.switchToWindowWithTitle( - WINDOW_TITLES.Dialog, - windowHandles, - ); - - await driver.findElement({ - css: 'p', - text: 'Example `personal_sign` message', - }); - - await driver.clickElement('[data-testid="confirm-footer-button"]'); - - await verifyAndAssertPersonalMessage(driver, publicAddress); - }, - ); - }); - it('can queue multiple personal signs and confirm', async function () { await withFixtures( { diff --git a/test/e2e/tests/signature/signature-request.spec.js b/test/e2e/tests/signature/signature-request.spec.js index 716ee1f98d95..7d37da3de0db 100644 --- a/test/e2e/tests/signature/signature-request.spec.js +++ b/test/e2e/tests/signature/signature-request.spec.js @@ -337,64 +337,6 @@ describe('Sign Typed Data Signature Request', function () { }); describe('Redesigned confirmation screens', function () { - testData.forEach((data) => { - it(`can initiate and confirm a Signature Request of ${data.type}`, async function () { - await withFixtures( - { - dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() - .build(), - ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), - }, - async ({ driver, ganacheServer }) => { - const addresses = await ganacheServer.getAccounts(); - const publicAddress = addresses[0]; - await unlockWallet(driver); - - await openDapp(driver); - - // creates a sign typed data signature request - await driver.clickElement(data.buttonId); - - await driver.waitUntilXWindowHandles(3); - let windowHandles = await driver.getAllWindowHandles(); - await driver.switchToWindowWithTitle( - WINDOW_TITLES.Dialog, - windowHandles, - ); - - await verifyAndAssertRedesignedSignTypedData( - driver, - data.expectedMessage, - ); - - // Approve signing typed data - await finalizeSignatureRequest( - driver, - '.confirm-scroll-to-bottom__button', - 'Confirm', - ); - await driver.waitUntilXWindowHandles(2); - windowHandles = await driver.getAllWindowHandles(); - - // switch to the Dapp and verify the signed address - await driver.switchToWindowWithTitle( - 'E2E Test Dapp', - windowHandles, - ); - await driver.clickElement(data.verifyId); - const recoveredAddress = await driver.findElement( - data.verifyResultId, - ); - - assert.equal(await recoveredAddress.getText(), publicAddress); - }, - ); - }); - }); - testData.forEach((data) => { it(`can queue multiple Signature Requests of ${data.type} and confirm`, async function () { await withFixtures( From 8a6f4f9198e19356ce545fb4d2561d86daabb6f5 Mon Sep 17 00:00:00 2001 From: Nidhi Kumari Date: Fri, 20 Dec 2024 10:59:13 +0000 Subject: [PATCH 4/9] fix: fixed truncation issue for long help text (#29269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR aims to fix the truncation issue in send flow. The decimal value in error message used to show all the decimal numbers after decimal. This PR updates the max value to be shown after decimals to be 4 which is also the standard decimal value we try to show in other places of code ## **Related issues** Fixes: #26766 ## **Manual testing steps** 1. Go to Send Flow 2. Try sending a token with large input than the available balance 3. Check the value in helptext doesn't overlap and only shows 4 numbers ## **Screenshots/Recordings** ### **Before** ![Screenshot 2024-12-17 at 11 17 19 AM](https://github.com/user-attachments/assets/8f9e39ba-60a6-4cd9-88f3-c7e56aa14e1c) ### **After** ![Screenshot 2024-12-17 at 11 18 05 AM](https://github.com/user-attachments/assets/08a6913e-13a1-40ad-bd48-ae8528412287) ## **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/main/.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/main/.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. --- .../asset-balance/asset-balance-text.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx b/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx index 3aec458341bd..29cbf7e40619 100644 --- a/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx +++ b/ui/components/multichain/asset-picker-amount/asset-balance/asset-balance-text.tsx @@ -48,6 +48,10 @@ export function AssetBalanceText({ const balanceString = hexToDecimal(asset.balance) || tokensWithBalances[0]?.string; + const showFixedBalanceString = balanceString?.includes('.') + ? balanceString.slice(0, balanceString.indexOf('.') + 5) // Include 4 digits after the decimal + : balanceString; + const balanceValue = useSelector(getSelectedAccountCachedBalance); const nativeTokenFiatBalance = useCurrencyDisplay(balanceValue, { @@ -135,7 +139,7 @@ export function AssetBalanceText({ return ( ); } From 3fd27a9e9a170da753963ddc751adc9cf585596d Mon Sep 17 00:00:00 2001 From: Vinicius Stevam <45455812+vinistevam@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:12:48 +0000 Subject: [PATCH 5/9] fix: `gasFeeEstimates` property undefined (#29312) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR fixes an issue created by Sentry where the property `gasFeeEstimates` is `undefined`. Added an early return when the property is `undefined` and covered the scenario with unit tests. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29312?quickstart=1) ## **Related issues** Fixes: https://github.com/MetaMask/metamask-extension/issues/27501 https://github.com/MetaMask/metamask-extension/issues/27241 ## **Manual testing steps** 1. Go to this test dapp 2. Block requests against gas-api 3. Start a send or contract interaction 4. Verify the console ## **Screenshots/Recordings** [block-gas-api.webm](https://github.com/user-attachments/assets/1c3cf638-905e-4df9-b875-84f0056979b1) ### **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/main/.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/main/.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. --- .../hooks/useTransactionFunction.test.js | 12 ++++++++++++ .../confirmations/hooks/useTransactionFunctions.js | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/pages/confirmations/hooks/useTransactionFunction.test.js b/ui/pages/confirmations/hooks/useTransactionFunction.test.js index 22984ba71a37..ec823d2acf70 100644 --- a/ui/pages/confirmations/hooks/useTransactionFunction.test.js +++ b/ui/pages/confirmations/hooks/useTransactionFunction.test.js @@ -164,4 +164,16 @@ describe('useMaxPriorityFeePerGasInput', () => { userFeeLevel: 'dappSuggested', }); }); + + it('returns early when gasFeeEstimates is undefined', () => { + const mockUpdateTransaction = jest + .spyOn(Actions, 'updateTransactionGasFees') + .mockImplementation(() => ({ type: '' })); + + const { result } = renderUseTransactionFunctions({ + gasFeeEstimates: undefined, + }); + result.current.updateTransactionUsingEstimate(GasRecommendations.low); + expect(mockUpdateTransaction).not.toHaveBeenCalled(); + }); }); diff --git a/ui/pages/confirmations/hooks/useTransactionFunctions.js b/ui/pages/confirmations/hooks/useTransactionFunctions.js index 17b5165da94e..96b41cd5e08e 100644 --- a/ui/pages/confirmations/hooks/useTransactionFunctions.js +++ b/ui/pages/confirmations/hooks/useTransactionFunctions.js @@ -201,9 +201,10 @@ export const useTransactionFunctions = ({ const updateTransactionUsingEstimate = useCallback( (gasFeeEstimateToUse) => { - if (!gasFeeEstimates[gasFeeEstimateToUse]) { + if (!gasFeeEstimates?.[gasFeeEstimateToUse]) { return; } + const { suggestedMaxFeePerGas, suggestedMaxPriorityFeePerGas } = gasFeeEstimates[gasFeeEstimateToUse]; updateTransaction({ From 02230c725bcc28b327df9b81c2198e74c9e875ea Mon Sep 17 00:00:00 2001 From: Salim TOUBAL Date: Fri, 20 Dec 2024 12:22:10 +0100 Subject: [PATCH 6/9] fix: remove Text in the Activity Empty State (#29318) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR updates the Activity empty state by removing the placeholder text. The change simplifies the UI, ensuring a cleaner and more visually appealing experience when no activity is present. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29318?quickstart=1) ## **Related issues** Fixes: #26669 ## **Manual testing steps** 1. Go to activity tab for any network where you don't have transactions 2. check the message ## **Screenshots/Recordings** ### **Before** Screenshot 2024-12-18 at 13 35 07 ### **After** Screenshot 2024-12-18 at 13 35 12 ## **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/main/.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/main/.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. --- app/_locales/am/messages.json | 3 - app/_locales/ar/messages.json | 3 - app/_locales/bg/messages.json | 3 - app/_locales/bn/messages.json | 3 - app/_locales/ca/messages.json | 3 - app/_locales/cs/messages.json | 3 - app/_locales/da/messages.json | 3 - app/_locales/de/messages.json | 3 - app/_locales/el/messages.json | 3 - app/_locales/en/messages.json | 10 -- app/_locales/en_GB/messages.json | 3 - app/_locales/es/messages.json | 3 - app/_locales/es_419/messages.json | 3 - app/_locales/et/messages.json | 3 - app/_locales/fa/messages.json | 3 - app/_locales/fi/messages.json | 3 - app/_locales/fil/messages.json | 3 - app/_locales/fr/messages.json | 3 - app/_locales/he/messages.json | 3 - app/_locales/hi/messages.json | 3 - app/_locales/hn/messages.json | 3 - app/_locales/hr/messages.json | 3 - app/_locales/ht/messages.json | 3 - app/_locales/hu/messages.json | 3 - app/_locales/id/messages.json | 3 - app/_locales/it/messages.json | 3 - app/_locales/ja/messages.json | 3 - app/_locales/kn/messages.json | 3 - app/_locales/ko/messages.json | 3 - app/_locales/lt/messages.json | 3 - app/_locales/lv/messages.json | 3 - app/_locales/ms/messages.json | 3 - app/_locales/nl/messages.json | 3 - app/_locales/no/messages.json | 3 - app/_locales/ph/messages.json | 3 - app/_locales/pl/messages.json | 3 - app/_locales/pt/messages.json | 3 - app/_locales/pt_BR/messages.json | 3 - app/_locales/ro/messages.json | 3 - app/_locales/ru/messages.json | 3 - app/_locales/sk/messages.json | 3 - app/_locales/sl/messages.json | 3 - app/_locales/sr/messages.json | 3 - app/_locales/sv/messages.json | 3 - app/_locales/sw/messages.json | 3 - app/_locales/ta/messages.json | 3 - app/_locales/th/messages.json | 3 - app/_locales/tl/messages.json | 3 - app/_locales/tr/messages.json | 3 - app/_locales/uk/messages.json | 3 - app/_locales/vi/messages.json | 3 - app/_locales/zh_CN/messages.json | 3 - app/_locales/zh_TW/messages.json | 3 - .../transaction/multiple-transactions.spec.js | 12 --- .../transaction-list.component.js | 97 ++++++++----------- .../transaction-list/transaction-list.test.js | 22 +++-- .../__snapshots__/asset-page.test.tsx.snap | 36 +------ ui/pages/asset/components/asset-page.tsx | 4 +- 58 files changed, 55 insertions(+), 282 deletions(-) diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json index ccb81d489af7..fac85861828c 100644 --- a/app/_locales/am/messages.json +++ b/app/_locales/am/messages.json @@ -428,9 +428,6 @@ "noConversionRateAvailable": { "message": "ምንም የልወጣ ተመን አይገኝም" }, - "noTransactions": { - "message": "ግብይቶች የሉዎትም" - }, "noWebcamFound": { "message": "የኮምፒዩተርዎ ካሜራ አልተገኘም። እባክዎ እንደገና ይሞክሩ።" }, diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json index 00685f39df87..33585243d016 100644 --- a/app/_locales/ar/messages.json +++ b/app/_locales/ar/messages.json @@ -444,9 +444,6 @@ "noConversionRateAvailable": { "message": "لا يوجد معدل تحويل متاح" }, - "noTransactions": { - "message": "لا توجد لديك معاملات" - }, "noWebcamFound": { "message": "لم يتم العثور على كاميرا ويب للكمبيوتر الخاص بك. حاول مرة اخرى." }, diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json index 2e51cf4b24b4..a6dbac690242 100644 --- a/app/_locales/bg/messages.json +++ b/app/_locales/bg/messages.json @@ -443,9 +443,6 @@ "noConversionRateAvailable": { "message": "Няма наличен процент на преобръщане" }, - "noTransactions": { - "message": "Нямате транзакции" - }, "noWebcamFound": { "message": "Уеб камерата на компютърa Ви не беше намерена. Моля, опитайте отново." }, diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json index 6f3bb290215d..1df74dc0e941 100644 --- a/app/_locales/bn/messages.json +++ b/app/_locales/bn/messages.json @@ -437,9 +437,6 @@ "noConversionRateAvailable": { "message": "কোনো বিনিময় হার উপলভ্য নয়" }, - "noTransactions": { - "message": "আপনার কোনো লেনদেন নেই" - }, "noWebcamFound": { "message": "আপনার কম্পিউটারের ওয়েবক্যাম খুঁজে পাওয়া যায়নি। অনুগ্রহ করে আবার চেষ্টা করুন।" }, diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json index c54e236d8a21..b988ae488143 100644 --- a/app/_locales/ca/messages.json +++ b/app/_locales/ca/messages.json @@ -431,9 +431,6 @@ "noConversionRateAvailable": { "message": "No hi ha cap tarifa de conversió disponible" }, - "noTransactions": { - "message": "No tens transaccions" - }, "noWebcamFound": { "message": "No s'ha trovat la webcam del teu ordinador. Si us plau prova de nou." }, diff --git a/app/_locales/cs/messages.json b/app/_locales/cs/messages.json index b6161b00a979..adf67dbda77d 100644 --- a/app/_locales/cs/messages.json +++ b/app/_locales/cs/messages.json @@ -210,9 +210,6 @@ "next": { "message": "Další" }, - "noTransactions": { - "message": "Žádné transakce" - }, "passwordNotLongEnough": { "message": "Heslo není dost dlouhé" }, diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json index e9c884f28dbb..080dd75f22d6 100644 --- a/app/_locales/da/messages.json +++ b/app/_locales/da/messages.json @@ -431,9 +431,6 @@ "noConversionRateAvailable": { "message": "Ingen tilgængelig omregningskurs" }, - "noTransactions": { - "message": "Du har ingen transaktioner" - }, "noWebcamFound": { "message": "Din computers webkamera blev ikke fundet. Prøv venligst igen." }, diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 04c45bd81348..9ad22ea297e0 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Nein, danke!" }, - "noTransactions": { - "message": "Keine Transaktionen" - }, "noWebcamFound": { "message": "Die Webcam Ihres Computers wurde nicht gefunden. Bitte versuchen Sie es erneut." }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 24da4df88460..aa5a819d532b 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Όχι, ευχαριστώ" }, - "noTransactions": { - "message": "Δεν έχετε καμιά συναλλαγή" - }, "noWebcamFound": { "message": "Η κάμερα του υπολογιστή σας δεν βρέθηκε. Προσπαθήστε ξανά." }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 2d0f8baa4158..e8d8fe2779f9 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3498,16 +3498,6 @@ "noThanks": { "message": "No thanks" }, - "noTransactions": { - "message": "You have no transactions" - }, - "noTransactionsChainIdMismatch": { - "message": "Please switch network to view transactions" - }, - "noTransactionsNetworkName": { - "message": "Please switch to $1 network to view transactions", - "description": "$1 represents the network name" - }, "noWebcamFound": { "message": "Your computer's webcam was not found. Please try again." }, diff --git a/app/_locales/en_GB/messages.json b/app/_locales/en_GB/messages.json index d242dc7e88f8..f66111064a90 100644 --- a/app/_locales/en_GB/messages.json +++ b/app/_locales/en_GB/messages.json @@ -3141,9 +3141,6 @@ "noThanks": { "message": "No thanks" }, - "noTransactions": { - "message": "You have no transactions" - }, "noWebcamFound": { "message": "Your computer's webcam was not found. Please try again." }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 0f24f5bc5b1a..2c6fa0a29885 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "No, gracias" }, - "noTransactions": { - "message": "No tiene transacciones" - }, "noWebcamFound": { "message": "No se encontró la cámara web del equipo. Vuelva a intentarlo." }, diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index 5d940a269091..4e82c875c760 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -1273,9 +1273,6 @@ "noConversionRateAvailable": { "message": "No hay tasa de conversión disponible" }, - "noTransactions": { - "message": "No tiene transacciones" - }, "noWebcamFound": { "message": "No se encontró la cámara web del equipo. Vuelva a intentarlo." }, diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json index c5f55c6d3327..a2a85b36d987 100644 --- a/app/_locales/et/messages.json +++ b/app/_locales/et/messages.json @@ -437,9 +437,6 @@ "noConversionRateAvailable": { "message": "Ühtegi vahetuskurssi pole saadaval" }, - "noTransactions": { - "message": "Teil ei ole tehinguid" - }, "noWebcamFound": { "message": "Teie arvuti veebikaamerat ei leitud. Proovige uuesti." }, diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json index 8399ecb91aec..3ec5211c2dfb 100644 --- a/app/_locales/fa/messages.json +++ b/app/_locales/fa/messages.json @@ -443,9 +443,6 @@ "noConversionRateAvailable": { "message": "هیچ نرخ تغییر موجود نمیباشد" }, - "noTransactions": { - "message": "شما هیچ معامله ندارید" - }, "noWebcamFound": { "message": "وب کم کمپیوتر تان پیدا نشد. لطفًا دوباره کوشش کنید." }, diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json index 5002c4eed2b3..c49ea583598d 100644 --- a/app/_locales/fi/messages.json +++ b/app/_locales/fi/messages.json @@ -443,9 +443,6 @@ "noConversionRateAvailable": { "message": "Vaihtokurssi ei saatavilla" }, - "noTransactions": { - "message": "Sinulla ei ole tapahtumia" - }, "noWebcamFound": { "message": "Tietokoneesi verkkokameraa ei löytynyt. Ole hyvä ja yritä uudestaan." }, diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json index abee8f9c0a5e..14f91bc5c16a 100644 --- a/app/_locales/fil/messages.json +++ b/app/_locales/fil/messages.json @@ -381,9 +381,6 @@ "noConversionRateAvailable": { "message": "Walang Presyo ng Palitan na Available" }, - "noTransactions": { - "message": "Wala kang mga transaksyon" - }, "noWebcamFound": { "message": "Hindi nakita ang webcam ng iyong computer. Pakisubukang muli." }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index ce615bddd591..1c8436635797 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Non merci" }, - "noTransactions": { - "message": "Aucune transaction" - }, "noWebcamFound": { "message": "La caméra de votre ordinateur n’a pas été trouvée. Veuillez réessayer." }, diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json index 3bd6b6b67408..c9360ff612de 100644 --- a/app/_locales/he/messages.json +++ b/app/_locales/he/messages.json @@ -440,9 +440,6 @@ "noConversionRateAvailable": { "message": "אין שער המרה זמין" }, - "noTransactions": { - "message": "אין לך עסקאות" - }, "noWebcamFound": { "message": "מצלמת הרשת של מחשבך לא נמצאה. נא לנסות שוב." }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 05488711d94f..8d115d214652 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "जी नहीं, धन्यवाद" }, - "noTransactions": { - "message": "आपके पास कोई ट्रांसेक्शन नहीं है" - }, "noWebcamFound": { "message": "आपके कंप्यूटर का वेबकैम नहीं मिला। कृपया फिर से कोशिश करें।" }, diff --git a/app/_locales/hn/messages.json b/app/_locales/hn/messages.json index f6bb938c4886..cecc6f26385b 100644 --- a/app/_locales/hn/messages.json +++ b/app/_locales/hn/messages.json @@ -193,9 +193,6 @@ "next": { "message": "अगला" }, - "noTransactions": { - "message": "कोई लेन-देन नहीं" - }, "pastePrivateKey": { "message": "यहां अपनी निजी कुंजी स्ट्रिंग चिपकाएं:", "description": "For importing an account from a private key" diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json index d1ca9bac8057..77b864172ae2 100644 --- a/app/_locales/hr/messages.json +++ b/app/_locales/hr/messages.json @@ -440,9 +440,6 @@ "noConversionRateAvailable": { "message": "Nijedan konverzijski tečaj nije dostupan" }, - "noTransactions": { - "message": "Nemate transkacija" - }, "noWebcamFound": { "message": "Mrežna kamera vašeg računala nije pronađena. Pokušajte ponovno." }, diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json index 8d0e7e703559..29df50803fa9 100644 --- a/app/_locales/ht/messages.json +++ b/app/_locales/ht/messages.json @@ -310,9 +310,6 @@ "noConversionRateAvailable": { "message": "Pa gen okenn Konvèsyon Disponib" }, - "noTransactions": { - "message": "Pa gen tranzaksyon" - }, "noWebcamFound": { "message": "Nou pakay jwenn webcam òdinatè ou. Tanpri eseye ankò." }, diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json index ee8699a64545..9255c752b4a7 100644 --- a/app/_locales/hu/messages.json +++ b/app/_locales/hu/messages.json @@ -440,9 +440,6 @@ "noConversionRateAvailable": { "message": "Nincs elérhető átváltási díj" }, - "noTransactions": { - "message": "Nincsenek tranzakciói" - }, "noWebcamFound": { "message": "Nem található számítógéped webkamerája. Kérünk, próbáld újra." }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index c918a6cc0fb0..fb488c423c61 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Tidak, terima kasih" }, - "noTransactions": { - "message": "Anda tidak memiliki transaksi" - }, "noWebcamFound": { "message": "Webcam komputer Anda tidak ditemukan. Harap coba lagi." }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index da1da37952f6..a88d710a6f81 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -1046,9 +1046,6 @@ "noConversionRateAvailable": { "message": "Tasso di conversione non disponibile" }, - "noTransactions": { - "message": "Nessuna Transazione" - }, "noWebcamFound": { "message": "La fotocamera del tuo computer non è stata trovata. Riprova." }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index ead0da87b7b8..35c812fa53f0 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "結構です" }, - "noTransactions": { - "message": "トランザクションがありません" - }, "noWebcamFound": { "message": "お使いのコンピューターのWebカメラが見つかりませんでした。もう一度お試しください。" }, diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json index 805de3c78fc0..148a820f5bfa 100644 --- a/app/_locales/kn/messages.json +++ b/app/_locales/kn/messages.json @@ -443,9 +443,6 @@ "noConversionRateAvailable": { "message": "ಯಾವುದೇ ಪರಿವರ್ತನೆ ದರ ಲಭ್ಯವಿಲ್ಲ" }, - "noTransactions": { - "message": "ನೀವು ಯಾವುದೇ ವಹಿವಾಟುಗಳನ್ನು ಹೊಂದಿಲ್ಲ" - }, "noWebcamFound": { "message": "ನಿಮ್ಮ ಕಂಪ್ಯೂಟರ್‌ನ ವೆಬ್‌ಕ್ಯಾಮ್ ಕಂಡುಬಂದಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ." }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index f22491040b4f..158a8a333c15 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "괜찮습니다" }, - "noTransactions": { - "message": "트랜잭션이 없습니다." - }, "noWebcamFound": { "message": "컴퓨터의 웹캠을 찾을 수 없습니다. 다시 시도하세요." }, diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json index cd034a303c4a..3e5586f39f50 100644 --- a/app/_locales/lt/messages.json +++ b/app/_locales/lt/messages.json @@ -443,9 +443,6 @@ "noConversionRateAvailable": { "message": "Nėra keitimo kurso" }, - "noTransactions": { - "message": "Neturite jokių operacijų" - }, "noWebcamFound": { "message": "Jūsų kompiuterio vaizdo kamera nerasta. Bandykite dar kartą." }, diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json index be5d43f4afd9..a1e166937385 100644 --- a/app/_locales/lv/messages.json +++ b/app/_locales/lv/messages.json @@ -443,9 +443,6 @@ "noConversionRateAvailable": { "message": "Konversijas kurss nav pieejams" }, - "noTransactions": { - "message": "Jums nav neviena darījuma." - }, "noWebcamFound": { "message": "Jūsu datora tīmekļa kamera netika atrasta. Lūdzu, mēģiniet vēlreiz." }, diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json index 20161aaf34da..3605febbc7d7 100644 --- a/app/_locales/ms/messages.json +++ b/app/_locales/ms/messages.json @@ -430,9 +430,6 @@ "noConversionRateAvailable": { "message": "Tiada Kadar Penukaran yang Tersedia" }, - "noTransactions": { - "message": "Anda tiada transaksi" - }, "noWebcamFound": { "message": "Webcam komputer anda tidak dijumpai. Sila cuba semula." }, diff --git a/app/_locales/nl/messages.json b/app/_locales/nl/messages.json index 7ac22889df2d..acf66091845f 100644 --- a/app/_locales/nl/messages.json +++ b/app/_locales/nl/messages.json @@ -183,9 +183,6 @@ "next": { "message": "volgende" }, - "noTransactions": { - "message": "Geen transacties" - }, "pastePrivateKey": { "message": "Plak hier uw privésleutelstring:", "description": "For importing an account from a private key" diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json index 67948544fdbf..8459d3ef3a5f 100644 --- a/app/_locales/no/messages.json +++ b/app/_locales/no/messages.json @@ -431,9 +431,6 @@ "noConversionRateAvailable": { "message": "Ingen konverteringsrate tilgjengelig " }, - "noTransactions": { - "message": "Du har ingen transaksjoner" - }, "noWebcamFound": { "message": "Datamaskinens webkamera ble ikke funnet. Vennligst prøv igjen." }, diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json index af995dbf5f5f..3dd9ae28d896 100644 --- a/app/_locales/ph/messages.json +++ b/app/_locales/ph/messages.json @@ -819,9 +819,6 @@ "noConversionRateAvailable": { "message": "Hindi Available ang Rate ng Conversion" }, - "noTransactions": { - "message": "Wala kang transaksyon" - }, "noWebcamFound": { "message": "Hindi nakita ang webcam ng iyong computer. Pakisubukan ulit." }, diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json index 5baeee9b8a00..f7f5111c9973 100644 --- a/app/_locales/pl/messages.json +++ b/app/_locales/pl/messages.json @@ -440,9 +440,6 @@ "noConversionRateAvailable": { "message": "Brak kursu waluty" }, - "noTransactions": { - "message": "Nie ma transakcji" - }, "noWebcamFound": { "message": "Twoja kamera nie została znaleziona. Spróbuj ponownie." }, diff --git a/app/_locales/pt/messages.json b/app/_locales/pt/messages.json index 50bf0a7d9996..e4fe18f9bab9 100644 --- a/app/_locales/pt/messages.json +++ b/app/_locales/pt/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Não, obrigado" }, - "noTransactions": { - "message": "Você não tem transações" - }, "noWebcamFound": { "message": "A webcam do seu computador não foi encontrada. Tente novamente." }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index 3c61987263b7..3c9c30147276 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -1273,9 +1273,6 @@ "noConversionRateAvailable": { "message": "Não há uma taxa de conversão disponível" }, - "noTransactions": { - "message": "Você não tem transações" - }, "noWebcamFound": { "message": "A webcam do seu computador não foi encontrada. Tente novamente." }, diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json index f149a976cf1a..db440fcfd264 100644 --- a/app/_locales/ro/messages.json +++ b/app/_locales/ro/messages.json @@ -434,9 +434,6 @@ "noConversionRateAvailable": { "message": "Nici o rată de conversie disponibilă" }, - "noTransactions": { - "message": "Nu aveți tranzacții" - }, "noWebcamFound": { "message": "Webcam-ul computerului dvs. nu a fost găsit. Vă rugăm să încercați din nou." }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index b1445befdd17..984223a22503 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Нет, спасибо" }, - "noTransactions": { - "message": "У вас нет транзакций" - }, "noWebcamFound": { "message": "Веб-камера вашего компьютера не найдена. Попробуйте еще раз." }, diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json index 1616af135d1a..a65bc68593f6 100644 --- a/app/_locales/sk/messages.json +++ b/app/_locales/sk/messages.json @@ -418,9 +418,6 @@ "noConversionRateAvailable": { "message": "Nie je k dispozícii žiadna sadzba konverzie" }, - "noTransactions": { - "message": "Žádné transakce" - }, "noWebcamFound": { "message": "Webová kamera vášho počítača sa nenašla. Skúste znova." }, diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json index dd73bc8e373f..ebc303e90c5c 100644 --- a/app/_locales/sl/messages.json +++ b/app/_locales/sl/messages.json @@ -431,9 +431,6 @@ "noConversionRateAvailable": { "message": "Menjalni tečaj ni na voljo" }, - "noTransactions": { - "message": "Nimate transakcij" - }, "noWebcamFound": { "message": "Spletna kamera ni najdena. Poskusite znova kasneje." }, diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json index c3986a0c0b98..1bbcd0754b92 100644 --- a/app/_locales/sr/messages.json +++ b/app/_locales/sr/messages.json @@ -434,9 +434,6 @@ "noConversionRateAvailable": { "message": "Nije dostupan kurs za konverziju" }, - "noTransactions": { - "message": "Nemate transakcije" - }, "noWebcamFound": { "message": "Nije pronađena veb kamera na vašem kompjuteru. Molimo vas pokušajte ponovo." }, diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json index 278515e908eb..818f1eb498d6 100644 --- a/app/_locales/sv/messages.json +++ b/app/_locales/sv/messages.json @@ -431,9 +431,6 @@ "noConversionRateAvailable": { "message": "Ingen omräkningskurs tillgänglig" }, - "noTransactions": { - "message": "Du har inga överföringar" - }, "noWebcamFound": { "message": "Din dators webbkamera hittades inte. Vänligen försök igen." }, diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json index 7c4a52f733fa..c33abcb51a18 100644 --- a/app/_locales/sw/messages.json +++ b/app/_locales/sw/messages.json @@ -425,9 +425,6 @@ "noConversionRateAvailable": { "message": "Hakuna Kiwango cha Ubadilishaji" }, - "noTransactions": { - "message": "Huna miamala." - }, "noWebcamFound": { "message": "Kamera yako ya kumpyuta haikupatikana. Tafadhali jaribu tena." }, diff --git a/app/_locales/ta/messages.json b/app/_locales/ta/messages.json index c5c36501cbb9..c5905e4e0bee 100644 --- a/app/_locales/ta/messages.json +++ b/app/_locales/ta/messages.json @@ -253,9 +253,6 @@ "next": { "message": "அடுத்தது" }, - "noTransactions": { - "message": "பரிவர்த்தனைகள் இல்லை" - }, "off": { "message": "ஆஃப்" }, diff --git a/app/_locales/th/messages.json b/app/_locales/th/messages.json index 08c9cb0df6dc..96166c6fb6ba 100644 --- a/app/_locales/th/messages.json +++ b/app/_locales/th/messages.json @@ -232,9 +232,6 @@ "next": { "message": "ถัดไป" }, - "noTransactions": { - "message": "ยังไม่มีรายการธุรกรรม" - }, "on": { "message": "เปิด" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 327be7838680..04650491ac24 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Salamat na lang" }, - "noTransactions": { - "message": "Wala kang transaksyon" - }, "noWebcamFound": { "message": "Hindi nakita ang webcam ng iyong computer. Pakisubukan ulit." }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 35ec9700e7d0..06cba108c1e0 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Hayır, istemiyorum" }, - "noTransactions": { - "message": "İşleminiz yok" - }, "noWebcamFound": { "message": "Bilgisayarınızın web kamerası bulunamadı. Lütfen tekrar deneyin." }, diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json index 36a181b03ab2..58787e9a2238 100644 --- a/app/_locales/uk/messages.json +++ b/app/_locales/uk/messages.json @@ -443,9 +443,6 @@ "noConversionRateAvailable": { "message": "Немає доступного обмінного курсу" }, - "noTransactions": { - "message": "У вас немає транзакцій" - }, "noWebcamFound": { "message": "Веб-камеру комп’ютера не знайдено. Повторіть спробу." }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 21b187998966..6f9b8264e82a 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "Không, cảm ơn" }, - "noTransactions": { - "message": "Bạn không có giao dịch nào" - }, "noWebcamFound": { "message": "Không tìm thấy webcam trên máy tính của bạn. Vui lòng thử lại." }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 635753750343..2f011db8e804 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -3304,9 +3304,6 @@ "noThanks": { "message": "不,谢谢" }, - "noTransactions": { - "message": "您没有任何交易" - }, "noWebcamFound": { "message": "未找到您电脑的网络摄像头。请重试。" }, diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index 87d8ebfb5520..7b4a425a03ad 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -812,9 +812,6 @@ "noConversionRateAvailable": { "message": "尚未有匯率比較值" }, - "noTransactions": { - "message": "尚未有交易" - }, "noWebcamFound": { "message": "無法搜尋到攝影鏡頭裝置。請再試一次" }, diff --git a/test/e2e/tests/transaction/multiple-transactions.spec.js b/test/e2e/tests/transaction/multiple-transactions.spec.js index b85937f5b6bd..72dbbe638e3b 100644 --- a/test/e2e/tests/transaction/multiple-transactions.spec.js +++ b/test/e2e/tests/transaction/multiple-transactions.spec.js @@ -122,12 +122,6 @@ describe('Multiple transactions', function () { '[data-testid="account-overview__activity-tab"]', ); - const isTransactionListEmpty = - await driver.isElementPresentAndVisible( - '.transaction-list__empty-text', - ); - assert.equal(isTransactionListEmpty, true); - // The previous isTransactionListEmpty wait already serves as the guard here for the assertElementNotPresent await driver.assertElementNotPresent( '.transaction-list__completed-transactions .activity-list-item', @@ -244,12 +238,6 @@ describe('Multiple transactions', function () { '[data-testid="account-overview__activity-tab"]', ); - const isTransactionListEmpty = - await driver.isElementPresentAndVisible( - '.transaction-list__empty-text', - ); - assert.equal(isTransactionListEmpty, true); - // The previous isTransactionListEmpty wait already serves as the guard here for the assertElementNotPresent await driver.assertElementNotPresent( '.transaction-list__completed-transactions .activity-list-item', diff --git a/ui/components/app/transaction-list/transaction-list.component.js b/ui/components/app/transaction-list/transaction-list.component.js index 27b0e1b5c60c..8ea78dee1948 100644 --- a/ui/components/app/transaction-list/transaction-list.component.js +++ b/ui/components/app/transaction-list/transaction-list.component.js @@ -13,10 +13,7 @@ import { nonceSortedCompletedTransactionsSelector, nonceSortedPendingTransactionsSelector, } from '../../../selectors/transactions'; -import { - getCurrentChainId, - getNetworkConfigurationsByChainId, -} from '../../../../shared/modules/selectors/networks'; +import { getCurrentChainId } from '../../../../shared/modules/selectors/networks'; import { getSelectedAccount, ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) @@ -143,7 +140,6 @@ export default function TransactionList({ hideTokenTransactions, tokenAddress, boxProps, - tokenChainId, }) { const [limit, setLimit] = useState(PAGE_INCREMENT); const t = useI18nContext(); @@ -156,16 +152,7 @@ export default function TransactionList({ ); const chainId = useSelector(getCurrentChainId); - const networkConfigurationsByChainId = useSelector( - getNetworkConfigurationsByChainId, - ); - const networkName = networkConfigurationsByChainId[tokenChainId]?.name; const selectedAccount = useSelector(getSelectedAccount); - const isChainIdMismatch = tokenChainId && tokenChainId !== chainId; - - const noTransactionsMessage = networkName - ? t('noTransactionsNetworkName', [networkName]) - : t('noTransactionsChainIdMismatch'); ///: BEGIN:ONLY_INCLUDE_IF(build-main,build-beta,build-flask) const shouldHideZeroBalanceTokens = useSelector( @@ -358,51 +345,43 @@ export default function TransactionList({ )} - {completedTransactions.length > 0 ? ( - completedTransactions - .map(removeIncomingTxsButToAnotherAddress) - .map(removeTxGroupsWithNoTx) - .filter(dateGroupsWithTransactionGroups) - .slice(0, limit) - .map((dateGroup) => { - return dateGroup.transactionGroups.map( - (transactionGroup, index) => { - return ( - - {renderDateStamp(index, dateGroup)} - {transactionGroup.initialTransaction - ?.isSmartTransaction ? ( - - ) : ( - - )} - - ); - }, - ); - }) - ) : ( - - - {isChainIdMismatch - ? noTransactionsMessage - : t('noTransactions')} - - - )} + {completedTransactions.length > 0 + ? completedTransactions + .map(removeIncomingTxsButToAnotherAddress) + .map(removeTxGroupsWithNoTx) + .filter(dateGroupsWithTransactionGroups) + .slice(0, limit) + .map((dateGroup) => { + return dateGroup.transactionGroups.map( + (transactionGroup, index) => { + return ( + + {renderDateStamp(index, dateGroup)} + {transactionGroup.initialTransaction + ?.isSmartTransaction ? ( + + ) : ( + + )} + + ); + }, + ); + }) + : null} {completedTransactions.length > limit && (