diff --git a/test/e2e/tests/import-tokens.spec.js b/test/e2e/tests/import-tokens.spec.js new file mode 100644 index 000000000..d86d5f097 --- /dev/null +++ b/test/e2e/tests/import-tokens.spec.js @@ -0,0 +1,61 @@ +const { strict: assert } = require('assert'); +const { + withFixtures, + convertToHexValue, + regularDelayMs, + unlockWallet, +} = require('../helpers'); +const FixtureBuilder = require('../fixture-builder'); + +describe('Import flow', function () { + const ganacheOptions = { + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + + it('allows importing multiple tokens from search', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder().build(), + ganacheOptions, + title: this.test.title, + }, + async ({ driver }) => { + await driver.navigate(); + await unlockWallet(driver); + + // Token list is only on mainnet + await driver.clickElement('[data-testid="network-display"]'); + await driver.clickElement({ text: 'Ethereum Mainnet', tag: 'button' }); + + // Wait for network to change and token list to load from state + await driver.delay(regularDelayMs); + + await driver.clickElement('[data-testid="import-token-button"]'); + await driver.fill('input[placeholder="Search tokens"]', 'cha'); + + await driver.clickElement('.token-list__token'); + await driver.clickElement('.token-list__token:nth-of-type(2)'); + await driver.clickElement('.token-list__token:nth-of-type(3)'); + + await driver.clickElement({ css: 'button', text: 'Next' }); + await driver.clickElement({ css: 'button', text: 'Import' }); + + await driver.clickElement('.asset-breadcrumb'); + + // Wait for "loading tokens" to be gone + await driver.waitForElementNotPresent( + '[data-testid="token-list-loading-message"]', + ); + + const items = await driver.findElements('.multichain-token-list-item'); + assert.equal(items.length, 4); + }, + ); + }); +}); diff --git a/ui/components/app/token-list/token-list.js b/ui/components/app/token-list/token-list.js index 2ed841033..44d801cef 100644 --- a/ui/components/app/token-list/token-list.js +++ b/ui/components/app/token-list/token-list.js @@ -36,6 +36,7 @@ export default function TokenList({ onTokenClick }) { alignItems={AlignItems.center} justifyContent={JustifyContent.center} padding={7} + data-testid="token-list-loading-message" > {t('loadingTokens')} diff --git a/ui/pages/confirm-import-token/confirm-import-token.js b/ui/pages/confirm-import-token/confirm-import-token.js index 61bc41ff4..e45ade9da 100644 --- a/ui/pages/confirm-import-token/confirm-import-token.js +++ b/ui/pages/confirm-import-token/confirm-import-token.js @@ -12,7 +12,7 @@ import { I18nContext } from '../../contexts/i18n'; import { MetaMetricsContext } from '../../contexts/metametrics'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { getPendingTokens } from '../../ducks/metamask/metamask'; -import { addTokens, clearPendingTokens } from '../../store/actions'; +import { addImportedTokens, clearPendingTokens } from '../../store/actions'; import { MetaMetricsEventCategory, MetaMetricsEventName, @@ -37,9 +37,9 @@ const ConfirmImportToken = () => { const pendingTokens = useSelector(getPendingTokens); const handleAddTokens = useCallback(async () => { - await dispatch(addTokens(pendingTokens)); - const addedTokenValues = Object.values(pendingTokens); + await dispatch(addImportedTokens(addedTokenValues)); + const firstTokenAddress = addedTokenValues?.[0].address?.toLowerCase(); addedTokenValues.forEach((pendingToken) => { diff --git a/ui/pages/confirm-import-token/confirm-import-token.test.js b/ui/pages/confirm-import-token/confirm-import-token.test.js index a4c9d824a..c756e8983 100644 --- a/ui/pages/confirm-import-token/confirm-import-token.test.js +++ b/ui/pages/confirm-import-token/confirm-import-token.test.js @@ -5,7 +5,7 @@ import { ASSET_ROUTE, IMPORT_TOKEN_ROUTE, } from '../../helpers/constants/routes'; -import { addTokens, clearPendingTokens } from '../../store/actions'; +import { addImportedTokens, clearPendingTokens } from '../../store/actions'; import configureStore from '../../store/store'; import { renderWithProvider } from '../../../test/jest'; import ConfirmImportToken from '.'; @@ -26,7 +26,7 @@ const MOCK_PENDING_TOKENS = { }; jest.mock('../../store/actions', () => ({ - addTokens: jest.fn().mockReturnValue({ type: 'test' }), + addImportedTokens: jest.fn().mockReturnValue({ type: 'test' }), clearPendingTokens: jest .fn() .mockReturnValue({ type: 'CLEAR_PENDING_TOKENS' }), @@ -118,7 +118,7 @@ describe('ConfirmImportToken Component', () => { await fireEvent.click(importTokensBtn); - expect(addTokens).toHaveBeenCalled(); + expect(addImportedTokens).toHaveBeenCalled(); expect(clearPendingTokens).toHaveBeenCalled(); expect(mockHistoryPush).toHaveBeenCalledTimes(1); expect(mockHistoryPush).toHaveBeenCalledWith( diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 28ef4943c..7bb50726a 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -2043,25 +2043,6 @@ export async function getTokenStandardAndDetails( ]); } -export function addTokens( - tokens: Token[] | { [address: string]: Token }, -): ThunkAction { - return (dispatch: MetaMaskReduxDispatch) => { - if (Array.isArray(tokens)) { - return Promise.all( - tokens.map(({ address, symbol, decimals }) => - dispatch(addToken(address, symbol, decimals)), - ), - ); - } - return Promise.all( - Object.entries(tokens).map(([_, { address, symbol, decimals }]) => - dispatch(addToken(address, symbol, decimals)), - ), - ); - }; -} - export function clearPendingTokens(): Action { return { type: actionConstants.CLEAR_PENDING_TOKENS,