diff --git a/test/e2e/page-objects/pages/home/asset-list.ts b/test/e2e/page-objects/pages/home/asset-list.ts index 9db896e3a035..8c120b9879b7 100644 --- a/test/e2e/page-objects/pages/home/asset-list.ts +++ b/test/e2e/page-objects/pages/home/asset-list.ts @@ -24,6 +24,11 @@ class AssetListPage { private readonly currentNetworksTotal = `${this.currentNetworkOption} [data-testid="account-value-and-suffix"]`; + private readonly customTokenModalOption = { + text: 'Custom token', + tag: 'button', + }; + private readonly hideTokenButton = '[data-testid="asset-options__hide"]'; private readonly hideTokenConfirmationButton = @@ -43,16 +48,42 @@ class AssetListPage { private readonly networksToggle = '[data-testid="sort-by-networks"]'; + private sortByAlphabetically = '[data-testid="sortByAlphabetically"]'; + + private sortByDecliningBalance = '[data-testid="sortByDecliningBalance"]'; + + private sortByPopoverToggle = '[data-testid="sort-by-popover-toggle"]'; + + private readonly tokenAddressInput = + '[data-testid="import-tokens-modal-custom-address"]'; + private readonly tokenAmountValue = '[data-testid="multichain-token-list-item-value"]'; + private readonly tokenImportedSuccessMessage = { + text: 'Token imported', + tag: 'h6', + }; + private readonly tokenListItem = '[data-testid="multichain-token-list-button"]'; private readonly tokenOptionsButton = '[data-testid="import-token-button"]'; + private tokenPercentage(address: string): string { + return `[data-testid="token-increase-decrease-percentage-${address}"]`; + } + private readonly tokenSearchInput = 'input[placeholder="Search tokens"]'; + private readonly tokenSymbolInput = + '[data-testid="import-tokens-modal-custom-symbol"]'; + + private readonly modalWarningBanner = 'div.mm-banner-alert--severity-warning'; + + private readonly tokenIncreaseDecreaseValue = + '[data-testid="token-increase-decrease-value"]'; + constructor(driver: Driver) { this.driver = driver; } @@ -103,6 +134,29 @@ class AssetListPage { return assets.length; } + async getTokenListNames(): Promise { + console.log(`Retrieving the list of token names`); + const tokenElements = await this.driver.findElements(this.tokenListItem); + const tokenNames = await Promise.all( + tokenElements.map(async (element) => { + return await element.getText(); + }), + ); + return tokenNames; + } + + async sortTokenList( + sortBy: 'alphabetically' | 'decliningBalance', + ): Promise { + console.log(`Sorting the token list by ${sortBy}`); + await this.driver.clickElement(this.sortByPopoverToggle); + if (sortBy === 'alphabetically') { + await this.driver.clickElement(this.sortByAlphabetically); + } else if (sortBy === 'decliningBalance') { + await this.driver.clickElement(this.sortByDecliningBalance); + } + } + /** * Hides a token by clicking on the token name, and confirming the hide modal. * @@ -119,6 +173,22 @@ class AssetListPage { ); } + async importCustomToken(tokenAddress: string, symbol: string): Promise { + console.log(`Creating custom token ${symbol} on homepage`); + await this.driver.clickElement(this.tokenOptionsButton); + await this.driver.clickElement(this.importTokensButton); + await this.driver.waitForSelector(this.importTokenModalTitle); + await this.driver.clickElement(this.customTokenModalOption); + await this.driver.waitForSelector(this.modalWarningBanner); + await this.driver.fill(this.tokenAddressInput, tokenAddress); + await this.driver.fill(this.tokenSymbolInput, symbol); + await this.driver.clickElement(this.importTokensNextButton); + await this.driver.clickElementAndWaitToDisappear( + this.confirmImportTokenButton, + ); + await this.driver.waitForSelector(this.tokenImportedSuccessMessage); + } + async importTokenBySearch(tokenName: string) { console.log(`Import token ${tokenName} on homepage by search`); await this.driver.clickElement(this.tokenOptionsButton); @@ -133,6 +203,24 @@ class AssetListPage { ); } + async importMultipleTokensBySearch(tokenNames: string[]) { + console.log( + `Importing tokens ${tokenNames.join(', ')} on homepage by search`, + ); + await this.driver.clickElement(this.tokenOptionsButton); + await this.driver.clickElement(this.importTokensButton); + await this.driver.waitForSelector(this.importTokenModalTitle); + + for (const name of tokenNames) { + await this.driver.fill(this.tokenSearchInput, name); + await this.driver.clickElement({ text: name, tag: 'p' }); + } + await this.driver.clickElement(this.importTokensNextButton); + await this.driver.clickElementAndWaitToDisappear( + this.confirmImportTokenButton, + ); + } + async openNetworksFilter(): Promise { console.log(`Opening the network filter`); await this.driver.clickElement(this.networksToggle); @@ -235,6 +323,71 @@ class AssetListPage { `Expected number of token items ${expectedNumber} is displayed.`, ); } + + /** + * Checks if the token's general increase or decrease percentage is displayed correctly + * + * @param address - The token address to check + * @param expectedChange - The expected change percentage value (e.g. '+0.02%' or '-0.03%') + */ + async check_tokenGeneralChangePercentage( + address: string, + expectedChange: string, + ): Promise { + console.log( + `Checking token general change percentage for address ${address}`, + ); + const isPresent = await this.driver.isElementPresentAndVisible({ + css: this.tokenPercentage(address), + text: expectedChange, + }); + if (!isPresent) { + throw new Error( + `Token general change percentage ${expectedChange} not found for address ${address}`, + ); + } + } + + /** + * Checks if the token's percentage change element does not exist + * + * @param address - The token address to check + */ + async check_tokenGeneralChangePercentageNotPresent( + address: string, + ): Promise { + console.log( + `Checking token general change percentage is not present for address ${address}`, + ); + const isPresent = await this.driver.isElementPresent({ + css: this.tokenPercentage(address), + }); + if (isPresent) { + throw new Error( + `Token general change percentage element should not exist for address ${address}`, + ); + } + } + + /** + * Checks if the token's general increase or decrease value is displayed correctly + * + * @param expectedChangeValue - The expected change value (e.g. '+$50.00' or '-$30.00') + */ + async check_tokenGeneralChangeValue( + expectedChangeValue: string, + ): Promise { + console.log(`Checking token general change value ${expectedChangeValue}`); + const isPresent = await this.driver.isElementPresentAndVisible({ + css: this.tokenIncreaseDecreaseValue, + text: expectedChangeValue, + }); + if (!isPresent) { + throw new Error( + `Token general change value ${expectedChangeValue} not found`, + ); + } + } } export default AssetListPage; diff --git a/test/e2e/page-objects/pages/send/send-token-page.ts b/test/e2e/page-objects/pages/send/send-token-page.ts index 3c1d96618556..6b3dd2a78d78 100644 --- a/test/e2e/page-objects/pages/send/send-token-page.ts +++ b/test/e2e/page-objects/pages/send/send-token-page.ts @@ -40,6 +40,9 @@ class SendTokenPage { private readonly toastText = '.toast-text'; + private readonly warning = + '[data-testid="send-warning"] .mm-box--min-width-0 span'; + constructor(driver: Driver) { this.driver = driver; } @@ -196,6 +199,22 @@ class SendTokenPage { text: address, }); } + + /** + * Verifies that a specific warning message is displayed on the send token screen. + * + * @param warningText - The expected warning text to validate against. + * @returns A promise that resolves if the warning message matches the expected text. + * @throws Assertion error if the warning message does not match the expected text. + */ + async check_warningMessage(warningText: string): Promise { + console.log(`Checking if warning message "${warningText}" is displayed`); + await this.driver.waitForSelector({ + css: this.warning, + text: warningText, + }); + console.log('Warning message validation successful'); + } } export default SendTokenPage; diff --git a/test/e2e/page-objects/pages/token-overview-page.ts b/test/e2e/page-objects/pages/token-overview-page.ts new file mode 100644 index 000000000000..46e93ed490c7 --- /dev/null +++ b/test/e2e/page-objects/pages/token-overview-page.ts @@ -0,0 +1,54 @@ +import { Driver } from '../../webdriver/driver'; + +class TokenOverviewPage { + private driver: Driver; + + private readonly receiveButton = { + text: 'Receive', + css: '.icon-button', + }; + + private readonly sendButton = { + text: 'Send', + css: '.icon-button', + }; + + private readonly swapButton = { + text: 'Swap', + css: '.icon-button', + }; + + constructor(driver: Driver) { + this.driver = driver; + } + + async check_pageIsLoaded(): Promise { + try { + await this.driver.waitForMultipleSelectors([ + this.sendButton, + this.swapButton, + ]); + } catch (e) { + console.log( + 'Timeout while waiting for Token overview page to be loaded', + e, + ); + throw e; + } + console.log('Token overview page is loaded'); + } + + async clickReceive(): Promise { + await this.driver.clickElement(this.receiveButton); + } + + async clickSend(): Promise { + await this.driver.clickElement(this.sendButton); + } + + async clickSwap(): Promise { + await this.driver.clickElement(this.swapButton); + } +} + +export default TokenOverviewPage; diff --git a/test/e2e/tests/tokens/import-tokens.spec.js b/test/e2e/tests/tokens/import-tokens.spec.ts similarity index 54% rename from test/e2e/tests/tokens/import-tokens.spec.js rename to test/e2e/tests/tokens/import-tokens.spec.ts index b15c9ffb100a..0a840a1ee304 100644 --- a/test/e2e/tests/tokens/import-tokens.spec.js +++ b/test/e2e/tests/tokens/import-tokens.spec.ts @@ -1,13 +1,16 @@ -const { strict: assert } = require('assert'); -const { +import AssetListPage from '../../page-objects/pages/home/asset-list'; +import HomePage from '../../page-objects/pages/home/homepage'; + +import { defaultGanacheOptions, withFixtures, unlockWallet, -} = require('../../helpers'); -const FixtureBuilder = require('../../fixture-builder'); +} from '../../helpers'; +import FixtureBuilder from '../../fixture-builder'; +import { Mockttp } from '../../mock-e2e'; describe('Import flow', function () { - async function mockPriceFetch(mockServer) { + async function mockPriceFetch(mockServer: Mockttp) { return [ await mockServer .forGet('https://price.api.cx.metamask.io/v2/chains/1/spot-prices') @@ -60,47 +63,27 @@ describe('Import flow', function () { }) .build(), ganacheOptions: defaultGanacheOptions, - title: this.test.fullTitle(), + title: this.test?.fullTitle(), testSpecificMock: mockPriceFetch, }, async ({ driver }) => { await unlockWallet(driver); - await driver.assertElementNotPresent('.loading-overlay'); - - await driver.clickElement('[data-testid="import-token-button"]'); - await driver.clickElement('[data-testid="importTokens"]'); - - await driver.fill('input[placeholder="Search tokens"]', 'cha'); - - await driver.clickElement('.token-list__token_component'); - await driver.clickElement( - '.token-list__token_component:nth-of-type(2)', - ); - await driver.clickElement( - '.token-list__token_component:nth-of-type(3)', - ); - - await driver.clickElement('[data-testid="import-tokens-button-next"]'); - await driver.clickElement( - '[data-testid="import-tokens-modal-import-button"]', - ); - - // Wait for "loading tokens" to be gone - await driver.assertElementNotPresent( - '[data-testid="token-list-loading-message"]', - ); - - await driver.assertElementNotPresent( - '[data-testid="token-list-loading-message"]', - ); - - await driver.clickElement('[data-testid="sort-by-networks"]'); - await driver.clickElement('[data-testid="network-filter-current"]'); + const homePage = new HomePage(driver); + const assetListPage = new AssetListPage(driver); + await homePage.check_pageIsLoaded(); + await assetListPage.importMultipleTokensBySearch([ + 'CHAIN', + 'CHANGE', + 'CHAI', + ]); - const expectedTokenListElementsAreFound = - await driver.elementCountBecomesN('.multichain-token-list-item', 4); - assert.equal(expectedTokenListElementsAreFound, true); + const tokenList = new AssetListPage(driver); + await tokenList.check_tokenItemNumber(5); // Linea & Mainnet Eth + await tokenList.check_tokenIsDisplayed('Ethereum'); + await tokenList.check_tokenIsDisplayed('Chain Games'); + await tokenList.check_tokenIsDisplayed('Changex'); + await tokenList.check_tokenIsDisplayed('Chai'); }, ); }); diff --git a/test/e2e/tests/tokens/send-erc20-to-contract.spec.js b/test/e2e/tests/tokens/send-erc20-to-contract.spec.js deleted file mode 100644 index 6e94b6377e67..000000000000 --- a/test/e2e/tests/tokens/send-erc20-to-contract.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -const { strict: assert } = require('assert'); -const { - defaultGanacheOptions, - withFixtures, - unlockWallet, -} = require('../../helpers'); -const { SMART_CONTRACTS } = require('../../seeder/smart-contracts'); -const FixtureBuilder = require('../../fixture-builder'); - -describe('Send ERC20 token to contract address', function () { - const smartContract = SMART_CONTRACTS.HST; - - it('should display the token contract warning to the user', async function () { - await withFixtures( - { - dapp: true, - fixtures: new FixtureBuilder().withTokensControllerERC20().build(), - ganacheOptions: defaultGanacheOptions, - smartContract, - title: this.test.fullTitle(), - }, - async ({ driver, contractRegistry }) => { - const contractAddress = await contractRegistry.getContractAddress( - smartContract, - ); - await unlockWallet(driver); - - // Send TST - await driver.clickElement( - '[data-testid="account-overview__asset-tab"]', - ); - await driver.clickElement( - '[data-testid="multichain-token-list-button"]', - ); - await driver.clickElement('[data-testid="coin-overview-send"]'); - - // Type contract address - await driver.fill( - 'input[placeholder="Enter public address (0x) or domain name"]', - contractAddress, - ); - - // Verify warning - const warningText = - 'Warning: you are about to send to a token contract which could result in a loss of funds. Learn more'; - const warning = await driver.findElement( - '[data-testid="send-warning"] .mm-box--min-width-0 span', - ); - assert.equal(await warning.getText(), warningText); - }, - ); - }); -}); diff --git a/test/e2e/tests/tokens/send-erc20-to-contract.spec.ts b/test/e2e/tests/tokens/send-erc20-to-contract.spec.ts new file mode 100644 index 000000000000..5c7b59cbc215 --- /dev/null +++ b/test/e2e/tests/tokens/send-erc20-to-contract.spec.ts @@ -0,0 +1,52 @@ +import { + defaultGanacheOptions, + withFixtures, + unlockWallet, +} from '../../helpers'; +import { SMART_CONTRACTS } from '../../seeder/smart-contracts'; +import FixtureBuilder from '../../fixture-builder'; + +import AssetListPage from '../../page-objects/pages/home/asset-list'; +import HomePage from '../../page-objects/pages/home/homepage'; +import SendTokenPage from '../../page-objects/pages/send/send-token-page'; +import TokenOverviewPage from '../../page-objects/pages/token-overview-page'; + +describe('Send ERC20 token to contract address', function () { + const smartContract = SMART_CONTRACTS.HST; + + it('should display the token contract warning to the user', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder().withTokensControllerERC20().build(), + ganacheOptions: defaultGanacheOptions, + smartContract, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }) => { + const contractAddress: string = + await contractRegistry.getContractAddress(smartContract); + await unlockWallet(driver); + + const homePage = new HomePage(driver); + const assetListPage = new AssetListPage(driver); + await homePage.check_pageIsLoaded(); + await assetListPage.clickOnAsset('TST'); + + // Send TST + const tokenOverviewPage = new TokenOverviewPage(driver); + await tokenOverviewPage.check_pageIsLoaded(); + await tokenOverviewPage.clickSend(); + + const sendTokenPage = new SendTokenPage(driver); + await sendTokenPage.check_pageIsLoaded(); + await sendTokenPage.fillRecipient(contractAddress); + + // Verify warning + const warningText = + 'Warning: you are about to send to a token contract which could result in a loss of funds. Learn more'; + await sendTokenPage.check_warningMessage(warningText); + }, + ); + }); +}); diff --git a/test/e2e/tests/tokens/token-list.spec.ts b/test/e2e/tests/tokens/token-list.spec.ts index f7b032c92a4c..4002142d595e 100644 --- a/test/e2e/tests/tokens/token-list.spec.ts +++ b/test/e2e/tests/tokens/token-list.spec.ts @@ -1,4 +1,3 @@ -import { strict as assert } from 'assert'; import { Mockttp } from 'mockttp'; import { Context } from 'mocha'; import { zeroAddress } from 'ethereumjs-util'; @@ -6,15 +5,17 @@ import { CHAIN_IDS } from '../../../../shared/constants/network'; import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils'; import FixtureBuilder from '../../fixture-builder'; import { - clickNestedButton, defaultGanacheOptions, unlockWallet, withFixtures, } from '../../helpers'; import { Driver } from '../../webdriver/driver'; +import HomePage from '../../page-objects/pages/home/homepage'; +import AssetListPage from '../../page-objects/pages/home/asset-list'; describe('Token List', function () { const chainId = CHAIN_IDS.MAINNET; + const lineaChainId = CHAIN_IDS.LINEA_MAINNET; const tokenAddress = '0x2EFA2Cb29C2341d8E5Ba7D3262C9e9d6f1Bf3711'; const symbol = 'foo'; @@ -26,88 +27,118 @@ describe('Token List', function () { }, }; - const importToken = async (driver: Driver) => { - await driver.clickElement(`[data-testid="import-token-button"]`); - await driver.clickElement(`[data-testid="importTokens"]`); - await clickNestedButton(driver, 'Custom token'); - await driver.fill( - '[data-testid="import-tokens-modal-custom-address"]', - tokenAddress, - ); - await driver.waitForSelector('p.mm-box--color-error-default'); - await driver.fill( - '[data-testid="import-tokens-modal-custom-symbol"]', - symbol, - ); - await driver.clickElement({ text: 'Next', tag: 'button' }); - await driver.clickElement( - '[data-testid="import-tokens-modal-import-button"]', - ); - await driver.findElement({ text: 'Token imported', tag: 'h6' }); + const mockEmptyPrices = async ( + mockServer: Mockttp, + chainIdToMock: string, + ) => { + return mockServer + .forGet( + `https://price.api.cx.metamask.io/v2/chains/${parseInt( + chainIdToMock, + 16, + )}/spot-prices`, + ) + .thenCallback(() => ({ + statusCode: 200, + json: {}, + })); + }; + + const mockEmptyHistoricalPrices = async ( + mockServer: Mockttp, + address: string, + ) => { + return mockServer + .forGet( + `https://price.api.cx.metamask.io/v1/chains/${chainId}/historical-prices/${address}`, + ) + .thenCallback(() => ({ + statusCode: 200, + json: {}, + })); + }; + + const mockSpotPrices = async ( + mockServer: Mockttp, + chainIdToMock: string, + prices: Record< + string, + { price: number; pricePercentChange1d: number; marketCap: number } + >, + ) => { + return mockServer + .forGet( + `https://price.api.cx.metamask.io/v2/chains/${parseInt( + chainIdToMock, + 16, + )}/spot-prices`, + ) + .thenCallback(() => ({ + statusCode: 200, + json: prices, + })); + }; + + const mockHistoricalPrices = async ( + mockServer: Mockttp, + address: string, + price: number, + ) => { + return mockServer + .forGet( + `https://price.api.cx.metamask.io/v1/chains/${chainId}/historical-prices/${toChecksumHexAddress( + address, + )}`, + ) + .thenCallback(() => ({ + statusCode: 200, + json: { + prices: [ + [1717566000000, price * 0.9], + [1717566322300, price], + [1717566611338, price * 1.1], + ], + }, + })); }; - it('should not shows percentage increase for an ERC20 token without prices available', async function () { + it('should not show percentage increase for an ERC20 token without prices available', async function () { await withFixtures( { ...fixtures, title: (this as Context).test?.fullTitle(), testSpecificMock: async (mockServer: Mockttp) => [ - // Mock no current price - await mockServer - .forGet( - `https://price.api.cx.metamask.io/v2/chains/${parseInt( - chainId, - 16, - )}/spot-prices`, - ) - .thenCallback(() => ({ - statusCode: 200, - json: {}, - })), - // Mock no historical prices - await mockServer - .forGet( - `https://price.api.cx.metamask.io/v1/chains/${chainId}/historical-prices/${tokenAddress}`, - ) - .thenCallback(() => ({ - statusCode: 200, - json: {}, - })), + await mockEmptyPrices(mockServer, chainId), + await mockEmptyPrices(mockServer, lineaChainId), + await mockEmptyHistoricalPrices(mockServer, tokenAddress), ], }, async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); - await importToken(driver); - // Verify native token increase - const testIdNative = `token-increase-decrease-percentage-${zeroAddress()}`; + const homePage = new HomePage(driver); + const assetListPage = new AssetListPage(driver); - // Verify native token increase - const testId = `token-increase-decrease-percentage-${tokenAddress}`; + await homePage.check_pageIsLoaded(); + await assetListPage.importCustomToken(tokenAddress, symbol); - const percentageNative = await ( - await driver.findElement(`[data-testid="${testIdNative}"]`) - ).getText(); - assert.equal(percentageNative, ''); - - const percentage = await ( - await driver.findElement(`[data-testid="${testId}"]`) - ).getText(); - assert.equal(percentage, ''); + await assetListPage.check_tokenGeneralChangePercentageNotPresent( + zeroAddress(), + ); + await assetListPage.check_tokenGeneralChangePercentageNotPresent( + tokenAddress, + ); }, ); }); it('shows percentage increase for an ERC20 token with prices available', async function () { const ethConversionInUsd = 10000; - - // Prices are in ETH const marketData = { price: 0.123, marketCap: 12, pricePercentChange1d: 0.05, }; - const marketDataNative = { price: 0.123, marketCap: 12, @@ -120,91 +151,35 @@ describe('Token List', function () { title: (this as Context).test?.fullTitle(), ethConversionInUsd, testSpecificMock: async (mockServer: Mockttp) => [ - // Mock current price - await mockServer - .forGet( - `https://price.api.cx.metamask.io/v2/chains/${parseInt( - chainId, - 16, - )}/spot-prices`, - ) - .thenCallback(() => ({ - statusCode: 200, - json: { - [zeroAddress()]: marketDataNative, - [tokenAddress.toLowerCase()]: marketData, - }, - })), - // Mock historical prices - await mockServer - .forGet( - `https://price.api.cx.metamask.io/v1/chains/${chainId}/historical-prices/${toChecksumHexAddress( - tokenAddress, - )}`, - ) - .thenCallback(() => ({ - statusCode: 200, - json: { - prices: [ - [1717566000000, marketData.price * 0.9], - [1717566322300, marketData.price], - [1717566611338, marketData.price * 1.1], - ], - }, - })), + await mockSpotPrices(mockServer, chainId, { + [zeroAddress()]: marketDataNative, + [tokenAddress.toLowerCase()]: marketData, + }), + await mockHistoricalPrices( + mockServer, + tokenAddress, + marketData.price, + ), ], }, async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); - await importToken(driver); - // Verify native token increase - const testIdBase = 'token-increase-decrease-percentage'; + const homePage = new HomePage(driver); + const assetListPage = new AssetListPage(driver); - const isETHIncreaseDOMPresentAndVisible = - await driver.isElementPresentAndVisible({ - css: `[data-testid="${testIdBase}-${zeroAddress()}"]`, - text: '+0.02%', - }); - assert.equal( - isETHIncreaseDOMPresentAndVisible, - true, - 'Invalid eth increase dom text content', - ); - - const isTokenIncreaseDecreasePercentageDOMPresent = - await driver.isElementPresentAndVisible({ - css: `[data-testid="${testIdBase}-${tokenAddress}"]`, - text: '+0.05%', - }); - assert.equal( - isTokenIncreaseDecreasePercentageDOMPresent, - true, - 'Invalid token increase dom text content', - ); + await homePage.check_pageIsLoaded(); + await assetListPage.importCustomToken(tokenAddress, symbol); - // check increase balance for native token eth - const isExpectedIncreaseDecreaseValueDOMPresentAndVisible = - await driver.isElementPresentAndVisible({ - css: '[data-testid="token-increase-decrease-value"]', - text: '+$50.00', - }); - assert.equal( - isExpectedIncreaseDecreaseValueDOMPresentAndVisible, - true, - 'Invalid increase-decrease-value dom text content', + await assetListPage.check_tokenGeneralChangePercentage( + zeroAddress(), + '+0.02%', ); - - const isExpectedIncreaseDecreasePercentageDOMPresentAndVisible = - await driver.isElementPresentAndVisible({ - css: '[data-testid="token-increase-decrease-percentage"]', - text: '(+0.02%)', - }); - assert.equal( - isExpectedIncreaseDecreasePercentageDOMPresentAndVisible, - true, - 'Invalid increase-decrease-percentage dom text content', + await assetListPage.check_tokenGeneralChangePercentage( + tokenAddress, + '+0.05%', ); + await assetListPage.check_tokenGeneralChangeValue('+$50.00'); }, ); }); diff --git a/test/e2e/tests/tokens/token-sort.spec.ts b/test/e2e/tests/tokens/token-sort.spec.ts index ff2d35a917dc..ddf9c33cceb1 100644 --- a/test/e2e/tests/tokens/token-sort.spec.ts +++ b/test/e2e/tests/tokens/token-sort.spec.ts @@ -3,102 +3,66 @@ import { Context } from 'mocha'; import { CHAIN_IDS } from '../../../../shared/constants/network'; import FixtureBuilder from '../../fixture-builder'; import { - clickNestedButton, defaultGanacheOptions, - regularDelayMs, unlockWallet, withFixtures, + largeDelayMs, } from '../../helpers'; import { Driver } from '../../webdriver/driver'; +import HomePage from '../../page-objects/pages/home/homepage'; +import AssetListPage from '../../page-objects/pages/home/asset-list'; -describe('Token List', function () { - const chainId = CHAIN_IDS.MAINNET; - const tokenAddress = '0x2EFA2Cb29C2341d8E5Ba7D3262C9e9d6f1Bf3711'; - const symbol = 'ABC'; +describe('Token List Sorting', function () { + const mainnetChainId = CHAIN_IDS.MAINNET; + const customTokenAddress = '0x2EFA2Cb29C2341d8E5Ba7D3262C9e9d6f1Bf3711'; + const customTokenSymbol = 'ABC'; - const fixtures = { - fixtures: new FixtureBuilder({ inputChainId: chainId }).build(), + const testFixtures = { + fixtures: new FixtureBuilder({ inputChainId: mainnetChainId }).build(), ganacheOptions: { ...defaultGanacheOptions, - chainId: parseInt(chainId, 16), + chainId: parseInt(mainnetChainId, 16), }, }; - const importToken = async (driver: Driver) => { - await driver.clickElement(`[data-testid="import-token-button"]`); - await driver.clickElement(`[data-testid="importTokens"]`); - await clickNestedButton(driver, 'Custom token'); - await driver.fill( - '[data-testid="import-tokens-modal-custom-address"]', - tokenAddress, - ); - await driver.waitForSelector('p.mm-box--color-error-default'); - await driver.fill( - '[data-testid="import-tokens-modal-custom-symbol"]', - symbol, - ); - await driver.delay(2000); - await driver.clickElement({ text: 'Next', tag: 'button' }); - await driver.clickElement( - '[data-testid="import-tokens-modal-import-button"]', - ); - await driver.findElement({ text: 'Token imported', tag: 'h6' }); - }; - - it('should sort alphabetically and by decreasing balance', async function () { + it('should sort tokens alphabetically and by decreasing balance', async function () { await withFixtures( { - ...fixtures, + ...testFixtures, title: (this as Context).test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { await unlockWallet(driver); - await importToken(driver); - - const tokenListBeforeSorting = await driver.findElements( - '[data-testid="multichain-token-list-button"]', - ); - const tokenSymbolsBeforeSorting = await Promise.all( - tokenListBeforeSorting.map(async (tokenElement) => { - return tokenElement.getText(); - }), - ); - - assert.ok(tokenSymbolsBeforeSorting[0].includes('Ethereum')); - await driver.clickElement('[data-testid="sort-by-popover-toggle"]'); - await driver.clickElement('[data-testid="sortByAlphabetically"]'); + const homePage = new HomePage(driver); + const assetListPage = new AssetListPage(driver); - await driver.delay(regularDelayMs); - const tokenListAfterSortingAlphabetically = await driver.findElements( - '[data-testid="multichain-token-list-button"]', - ); - const tokenListSymbolsAfterSortingAlphabetically = await Promise.all( - tokenListAfterSortingAlphabetically.map(async (tokenElement) => { - return tokenElement.getText(); - }), + await homePage.check_pageIsLoaded(); + await assetListPage.importCustomToken( + customTokenAddress, + customTokenSymbol, ); - assert.ok( - tokenListSymbolsAfterSortingAlphabetically[0].includes('ABC'), - ); + const initialTokenList = await assetListPage.getTokenListNames(); + assert.ok(initialTokenList[0].includes('Ethereum')); + await assetListPage.sortTokenList('alphabetically'); - await driver.clickElement('[data-testid="sort-by-popover-toggle"]'); - await driver.clickElement('[data-testid="sortByDecliningBalance"]'); - - await driver.delay(regularDelayMs); - const tokenListBeforeSortingByDecliningBalance = - await driver.findElements( - '[data-testid="multichain-token-list-button"]', - ); - - const tokenListAfterSortingByDecliningBalance = await Promise.all( - tokenListBeforeSortingByDecliningBalance.map(async (tokenElement) => { - return tokenElement.getText(); - }), + await driver.waitUntil( + async () => { + const sortedTokenList = await assetListPage.getTokenListNames(); + return sortedTokenList[0].includes(customTokenSymbol); + }, + { timeout: largeDelayMs, interval: 100 }, ); - assert.ok( - tokenListAfterSortingByDecliningBalance[0].includes('Ethereum'), + + await assetListPage.sortTokenList('decliningBalance'); + await driver.waitUntil( + async () => { + const sortedTokenListByBalance = + await assetListPage.getTokenListNames(); + return sortedTokenListByBalance[0].includes('Ethereum'); + }, + { timeout: largeDelayMs, interval: 100 }, ); }, );