diff --git a/test/e2e/mock-e2e.js b/test/e2e/mock-e2e.js
index f1975fbdc..a6cd0a70d 100644
--- a/test/e2e/mock-e2e.js
+++ b/test/e2e/mock-e2e.js
@@ -189,19 +189,10 @@ async function setupMocking(server, testSpecificMock) {
};
});
+ // It disables loading of token icons, e.g. this URL: https://static.metaswap.codefi.network/api/v1/tokenIcons/1337/0x0000000000000000000000000000000000000000.png
await server
.forGet(
- 'https://static.metaswap.codefi.network/api/v1/tokenIcons/1337/0x0d8775f648430679a709e98d2b0cb6250d2887ef.png',
- )
- .thenCallback(() => {
- return {
- statusCode: 200,
- };
- });
-
- await server
- .forGet(
- 'https://static.metaswap.codefi.network/api/v1/tokenIcons/1337/0x2efa2cb29c2341d8e5ba7d3262c9e9d6f1bf3711.png',
+ /^https:\/\/static\.metaswap\.codefi\.network\/api\/v1\/tokenIcons\/1337\/.*\.png/u,
)
.thenCallback(() => {
return {
diff --git a/test/e2e/run-all.js b/test/e2e/run-all.js
index ad538f3f1..2623f9ba9 100644
--- a/test/e2e/run-all.js
+++ b/test/e2e/run-all.js
@@ -51,8 +51,7 @@ async function main() {
if (!snaps) {
testPaths = [
...testPaths,
- // TODO: Enable the next line once the Swaps E2E tests are stable.
- // ...(await getTestPathsForTestDir(path.join(__dirname, 'swaps'))),
+ ...(await getTestPathsForTestDir(path.join(__dirname, 'swaps'))),
path.join(__dirname, 'metamask-ui.spec.js'),
];
}
diff --git a/test/e2e/swaps/shared.js b/test/e2e/swaps/shared.js
index 8eaea0766..1a2dc40a3 100644
--- a/test/e2e/swaps/shared.js
+++ b/test/e2e/swaps/shared.js
@@ -1,3 +1,5 @@
+const FixtureBuilder = require('../fixture-builder');
+
const ganacheOptions = {
accounts: [
{
@@ -8,6 +10,11 @@ const ganacheOptions = {
],
};
+const withFixturesOptions = {
+ fixtures: new FixtureBuilder().build(),
+ ganacheOptions,
+};
+
const loadSwaps = async (driver) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
@@ -28,19 +35,37 @@ const buildQuote = async (driver, options) => {
await driver.wait(async () => {
const tokens = await driver.findElements('.searchable-item-list__item');
return tokens.length > 1;
- }, 10000);
+ });
await driver.clickElement('.searchable-item-list__labels');
await driver.clickElement('.dropdown-input-pair__to');
- await driver.clickElement('[placeholder="Search name or paste address"]');
+ await driver.clickElement('input[data-testid="search-list-items"]');
await driver.fill(
- '[placeholder="Search name or paste address"]',
- options.swapTo,
+ 'input[data-testid="search-list-items"]',
+ options.swapTo || options.swapToContractAddress,
);
+ if (options.swapTo) {
+ await driver.wait(async () => {
+ const tokenNames = await driver.findElements(
+ '.searchable-item-list__primary-label',
+ );
+ if (tokenNames.length === 0) {
+ return false;
+ }
+ const tokenName = await tokenNames[0].getText();
+ return tokenName === options.swapTo;
+ });
+ }
+ if (options.swapToContractAddress) {
+ await driver.waitForSelector({
+ css: '.searchable-item-list__item button.btn-primary',
+ text: 'Import',
+ });
+ }
await driver.clickElement('.searchable-item-list__primary-label');
};
module.exports = {
- ganacheOptions,
+ withFixturesOptions,
loadSwaps,
buildQuote,
};
diff --git a/test/e2e/swaps/swap-eth.spec.js b/test/e2e/swaps/swap-eth.spec.js
index fb4d98b1d..d67c258d8 100644
--- a/test/e2e/swaps/swap-eth.spec.js
+++ b/test/e2e/swaps/swap-eth.spec.js
@@ -1,16 +1,13 @@
const { strict: assert } = require('assert');
const { withFixtures } = require('../helpers');
-const FixtureBuilder = require('../fixture-builder');
-const { ganacheOptions, loadSwaps, buildQuote } = require('./shared');
+const { withFixturesOptions, loadSwaps, buildQuote } = require('./shared');
describe('Swap Eth for another Token', function () {
it('Completes a Swap between Eth and Matic', async function () {
await withFixtures(
{
- fixtures: new FixtureBuilder().build(),
- ganacheOptions,
- failOnConsoleError: false,
+ ...withFixturesOptions,
title: this.test.title,
},
async ({ driver }) => {
diff --git a/test/e2e/swaps/swaps-notifications.spec.js b/test/e2e/swaps/swaps-notifications.spec.js
index d93e08a88..07c845f98 100644
--- a/test/e2e/swaps/swaps-notifications.spec.js
+++ b/test/e2e/swaps/swaps-notifications.spec.js
@@ -1,16 +1,14 @@
const { strict: assert } = require('assert');
const { withFixtures } = require('../helpers');
-const FixtureBuilder = require('../fixture-builder');
-const { ganacheOptions, loadSwaps, buildQuote } = require('./shared');
+
+const { withFixturesOptions, loadSwaps, buildQuote } = require('./shared');
describe('Swaps - notifications', function () {
it('tests notifications for verified token on 1 source and price difference', async function () {
await withFixtures(
{
- fixtures: new FixtureBuilder().build(),
- ganacheOptions,
- failOnConsoleError: false,
+ ...withFixturesOptions,
title: this.test.title,
},
async ({ driver }) => {
@@ -46,19 +44,52 @@ describe('Swaps - notifications', function () {
);
});
+ it('tests a notification for not enough balance', async function () {
+ await withFixtures(
+ {
+ ...withFixturesOptions,
+ title: this.test.title,
+ },
+ async ({ driver }) => {
+ await loadSwaps(driver);
+ await buildQuote(driver, {
+ amount: 50,
+ swapTo: 'USDC',
+ });
+ const reviewSwapButton = await driver.findElement(
+ '[data-testid="page-container-footer-next"]',
+ );
+ assert.equal(await reviewSwapButton.getText(), 'Review swap');
+ assert.equal(await reviewSwapButton.isEnabled(), true);
+ await reviewSwapButton.click();
+ await driver.waitForSelector({
+ css: '[class*="box--align-items-center"]',
+ text: 'Estimated gas fee',
+ });
+ await driver.waitForSelector({
+ css: '[class*="actionable-message__message"]',
+ text: 'You need 43.4467 more TESTETH to complete this swap',
+ });
+ const swapButton = await driver.findElement(
+ '[data-testid="page-container-footer-next"]',
+ );
+ assert.equal(await swapButton.getText(), 'Swap');
+ assert.equal(await swapButton.isEnabled(), false);
+ },
+ );
+ });
+
it('tests notifications for verified token on 0 sources and high slippage', async function () {
await withFixtures(
{
- fixtures: new FixtureBuilder().build(),
- ganacheOptions,
- failOnConsoleError: false,
+ ...withFixturesOptions,
title: this.test.title,
},
async ({ driver }) => {
await loadSwaps(driver);
await buildQuote(driver, {
amount: 2,
- swapTo: '0x72c9Fb7ED19D3ce51cea5C56B3e023cd918baaDf',
+ swapToContractAddress: '0x72c9Fb7ED19D3ce51cea5C56B3e023cd918baaDf',
});
await driver.waitForSelector({
css: '.popover-header__title',
@@ -96,41 +127,4 @@ describe('Swaps - notifications', function () {
},
);
});
-
- it('tests a notification for not enough balance', async function () {
- await withFixtures(
- {
- fixtures: new FixtureBuilder().build(),
- ganacheOptions,
- failOnConsoleError: false,
- title: this.test.title,
- },
- async ({ driver }) => {
- await loadSwaps(driver);
- await buildQuote(driver, {
- amount: 50,
- swapTo: 'USDC',
- });
- const reviewSwapButton = await driver.findElement(
- '[data-testid="page-container-footer-next"]',
- );
- assert.equal(await reviewSwapButton.getText(), 'Review swap');
- assert.equal(await reviewSwapButton.isEnabled(), true);
- await reviewSwapButton.click();
- await driver.waitForSelector({
- css: '[class*="box--align-items-center"]',
- text: 'Estimated gas fee',
- });
- await driver.waitForSelector({
- css: '[class*="actionable-message__message"]',
- text: 'You need 43.4467 more TESTETH to complete this swap',
- });
- const swapButton = await driver.findElement(
- '[data-testid="page-container-footer-next"]',
- );
- assert.equal(await swapButton.getText(), 'Swap');
- assert.equal(await swapButton.isEnabled(), false);
- },
- );
- });
});
diff --git a/test/jest/index.js b/test/jest/index.js
index 9e6510ca8..1b4174afa 100644
--- a/test/jest/index.js
+++ b/test/jest/index.js
@@ -1,4 +1,4 @@
-export { screen, fireEvent } from '@testing-library/react';
+export { screen, fireEvent, waitFor } from '@testing-library/react';
export { createSwapsMockStore } from './mock-store';
export { renderWithProvider } from './rendering';
export { setBackgroundConnection } from './background';
diff --git a/ui/pages/swaps/fee-card/fee-card.js b/ui/pages/swaps/fee-card/fee-card.js
index 3f0da0bb4..6883c57fa 100644
--- a/ui/pages/swaps/fee-card/fee-card.js
+++ b/ui/pages/swaps/fee-card/fee-card.js
@@ -192,5 +192,5 @@ FeeCard.propTypes = {
onQuotesClick: PropTypes.func.isRequired,
numberOfQuotes: PropTypes.number.isRequired,
chainId: PropTypes.string.isRequired,
- isBestQuote: PropTypes.bool.isRequired,
+ isBestQuote: PropTypes.bool,
};
diff --git a/ui/pages/swaps/import-token/import-token.js b/ui/pages/swaps/import-token/import-token.js
index 1376d81b6..e8e4f0f52 100644
--- a/ui/pages/swaps/import-token/import-token.js
+++ b/ui/pages/swaps/import-token/import-token.js
@@ -67,7 +67,7 @@ export default function ImportToken({
fontWeight={FONT_WEIGHT.BOLD}
boxProps={{ marginTop: 2, marginBottom: 3 }}
>
- {tokenForImport.name}
+ {tokenForImport.name || ''}
{t('contract')}:
- {tokenForImport.address}
+ {tokenForImport.address || ''}
diff --git a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js
index 4d4299486..079089ef3 100644
--- a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js
+++ b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js
@@ -17,6 +17,8 @@ const renderAdornment = () => (
);
+let timeoutIdForSearch;
+
export default function ListItemSearch({
onSearch,
error,
@@ -37,7 +39,6 @@ export default function ListItemSearch({
* @param {string} contractAddress
*/
const handleSearchTokenForImport = async (contractAddress) => {
- setSearchQuery(contractAddress);
try {
const token = await fetchToken(contractAddress, chainId);
if (token) {
@@ -60,22 +61,32 @@ export default function ListItemSearch({
};
const handleSearch = async (newSearchQuery) => {
- const trimmedNewSearchQuery = newSearchQuery.trim();
- const validHexAddress = isValidHexAddress(trimmedNewSearchQuery);
- const fuseSearchResult = fuseRef.current.search(newSearchQuery);
- const results =
- defaultToAll && newSearchQuery === '' ? listToSearch : fuseSearchResult;
- if (shouldSearchForImports && results.length === 0 && validHexAddress) {
- await handleSearchTokenForImport(trimmedNewSearchQuery);
- return;
- }
setSearchQuery(newSearchQuery);
- onSearch({
- searchQuery: newSearchQuery,
- results,
- });
+ if (timeoutIdForSearch) {
+ clearTimeout(timeoutIdForSearch);
+ }
+ timeoutIdForSearch = setTimeout(async () => {
+ timeoutIdForSearch = null;
+ const trimmedNewSearchQuery = newSearchQuery.trim();
+ const validHexAddress = isValidHexAddress(trimmedNewSearchQuery);
+ const fuseSearchResult = fuseRef.current.search(newSearchQuery);
+ const results =
+ defaultToAll && newSearchQuery === '' ? listToSearch : fuseSearchResult;
+ if (shouldSearchForImports && results.length === 0 && validHexAddress) {
+ await handleSearchTokenForImport(trimmedNewSearchQuery);
+ return;
+ }
+ onSearch({
+ searchQuery: newSearchQuery,
+ results,
+ });
+ }, 350);
};
+ useEffect(() => {
+ return () => clearTimeout(timeoutIdForSearch);
+ }, []);
+
useEffect(() => {
if (!fuseRef.current) {
fuseRef.current = new Fuse(listToSearch, {
@@ -128,6 +139,6 @@ ListItemSearch.propTypes = {
searchPlaceholderText: PropTypes.string,
defaultToAll: PropTypes.bool,
shouldSearchForImports: PropTypes.bool,
- searchQuery: PropTypes.func,
+ searchQuery: PropTypes.string,
setSearchQuery: PropTypes.func,
};
diff --git a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.test.js b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.test.js
index a6a1e476c..0f82c3a09 100644
--- a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.test.js
+++ b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.test.js
@@ -74,6 +74,7 @@ describe('ListItemSearch', () => {
});
it('changes the search query', () => {
+ jest.useFakeTimers();
const store = configureMockStore(middleware)(createSwapsMockStore());
const props = createProps();
const { getByTestId } = renderWithProvider(
@@ -82,6 +83,7 @@ describe('ListItemSearch', () => {
);
const input = getByTestId('search-list-items');
fireEvent.change(input, { target: { value: 'USD' } });
+ jest.runAllTimers();
expect(props.setSearchQuery).toHaveBeenCalledWith('USD');
expect(props.onSearch).toHaveBeenCalledWith({
searchQuery: 'USD',
@@ -90,6 +92,7 @@ describe('ListItemSearch', () => {
});
it('imports a token', async () => {
+ jest.useFakeTimers();
const store = configureMockStore(middleware)(createSwapsMockStore());
const props = createProps({ shouldSearchForImports: true });
const { getByTestId } = renderWithProvider(
@@ -98,6 +101,7 @@ describe('ListItemSearch', () => {
);
const input = getByTestId('search-list-items');
await fireEvent.change(input, { target: { value: token.address } });
+ await jest.runAllTimers();
expect(props.setSearchQuery).toHaveBeenCalledWith(token.address);
expect(props.onSearch).toHaveBeenCalledWith({
searchQuery: token.address,
diff --git a/ui/pages/swaps/searchable-item-list/searchable-item-list.js b/ui/pages/swaps/searchable-item-list/searchable-item-list.js
index e7fb04447..c02d5f70c 100644
--- a/ui/pages/swaps/searchable-item-list/searchable-item-list.js
+++ b/ui/pages/swaps/searchable-item-list/searchable-item-list.js
@@ -93,6 +93,6 @@ SearchableItemList.propTypes = {
hideItemIf: PropTypes.func,
listContainerClassName: PropTypes.string,
shouldSearchForImports: PropTypes.bool,
- searchQuery: PropTypes.func,
+ searchQuery: PropTypes.string,
setSearchQuery: PropTypes.func,
};
diff --git a/ui/pages/swaps/view-quote/view-quote.js b/ui/pages/swaps/view-quote/view-quote.js
index 09326c206..7897b61a7 100644
--- a/ui/pages/swaps/view-quote/view-quote.js
+++ b/ui/pages/swaps/view-quote/view-quote.js
@@ -741,7 +741,7 @@ export default function ViewQuote() {
const priceSlippageUnknownFiatValue =
!priceSlippageFromSource ||
!priceSlippageFromDestination ||
- usedQuote?.priceSlippage?.calculationError;
+ Boolean(usedQuote?.priceSlippage?.calculationError);
let priceDifferencePercentage = 0;
if (usedQuote?.priceSlippage?.ratio) {
@@ -1038,7 +1038,7 @@ export default function ViewQuote() {
}
hideCancel
disabled={isSwapButtonDisabled}
- className={isShowingWarning && 'view-quote__thin-swaps-footer'}
+ className={isShowingWarning ? 'view-quote__thin-swaps-footer' : ''}
showTopBorder
/>