diff --git a/cypress.config.ts b/cypress.config.ts index d2491fe956..2d8a9bc626 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ defaultCommandTimeout: 24000, // 2x average block time chromeWebSecurity: false, experimentalMemoryManagement: true, // better memory management, see https://github.com/cypress-io/cypress/pull/25462 - retries: { runMode: process.env.CYPRESS_RETRIES ? +process.env.CYPRESS_RETRIES : 2 }, + retries: { runMode: process.env.CYPRESS_RETRIES ? +process.env.CYPRESS_RETRIES : 1 }, video: false, // GH provides 2 CPUs, and cypress video eats one up, see https://github.com/cypress-io/cypress/issues/20468#issuecomment-1307608025 e2e: { async setupNodeEvents(on, config) { diff --git a/cypress/e2e/swap/swapFlowLogging.test.ts b/cypress/e2e/swap/logging.test.ts similarity index 100% rename from cypress/e2e/swap/swapFlowLogging.test.ts rename to cypress/e2e/swap/logging.test.ts diff --git a/cypress/e2e/swap/uniswapx.test.ts b/cypress/e2e/swap/uniswapx.test.ts index d42981047f..fa5e358b1c 100644 --- a/cypress/e2e/swap/uniswapx.test.ts +++ b/cypress/e2e/swap/uniswapx.test.ts @@ -1,18 +1,36 @@ import { ChainId, CurrencyAmount } from '@uniswap/sdk-core' +import { CyHttpMessages } from 'cypress/types/net-stubbing' import { FeatureFlag } from 'featureFlags' import { DAI, nativeOnChain, USDC_MAINNET } from '../../../src/constants/tokens' import { getTestSelector } from '../../utils' -const QuoteEndpoint = 'https://api.uniswap.org/v2/quote' const QuoteWhereUniswapXIsBetter = 'uniswapx/quote1.json' const QuoteWithEthInput = 'uniswapx/quote2.json' +const QuoteEndpoint = 'https://api.uniswap.org/v2/quote' const OrderSubmissionEndpoint = 'https://api.uniswap.org/v2/order' - const OrderStatusEndpoint = 'https://api.uniswap.org/v2/orders?swapper=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&orderHashes=0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19' +/** + * Stubs quote to return a quote for non-price requests + * Price quotes are blocked with 409, as the backend would not accept them regardless + */ +function stubNonPriceQuoteWith(fixture: string) { + cy.intercept(QuoteEndpoint, (req: CyHttpMessages.IncomingHttpRequest) => { + let body = req.body + if (typeof body === 'string') { + body = JSON.parse(body) + } + if (body.intent === 'pricing') { + req.reply({ statusCode: 409 }) + } else { + req.reply({ fixture }) + } + }).as('quote') +} + /** Stubs the provider to return a tx receipt corresponding to the mock filled uniswapx order's txHash */ function stubSwapTxReceipt() { cy.hardhat().then((hardhat) => { @@ -26,15 +44,16 @@ function stubSwapTxReceipt() { describe('UniswapX Toggle', () => { beforeEach(() => { - cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter }) + stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter) cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`, { featureFlags: [{ name: FeatureFlag.uniswapXDefaultEnabled, value: false }], }) }) - it('only displays uniswapx ui when setting is on', () => { + it('displays uniswapx ui when setting is on', () => { // Setup a swap cy.get('#swap-currency-input .token-amount-input').type('300') + cy.wait('@quote') // UniswapX UI should not be visible cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist') @@ -49,6 +68,7 @@ describe('UniswapX Toggle', () => { it('prompts opt-in if UniswapX is better', () => { // Setup a swap cy.get('#swap-currency-input .token-amount-input').type('300') + cy.wait('@quote') // UniswapX should not display in gas estimate row before opt-in cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist') @@ -72,7 +92,7 @@ describe('UniswapX Toggle', () => { describe('UniswapX Orders', () => { beforeEach(() => { - cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter }) + stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter) cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' }) cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' }) @@ -87,6 +107,8 @@ describe('UniswapX Orders', () => { it('can swap exact-in trades using uniswapX', () => { // Setup a swap cy.get('#swap-currency-input .token-amount-input').type('300') + cy.wait('@quote') + cy.contains('Try it now').click() // Submit uniswapx order signature @@ -106,6 +128,8 @@ describe('UniswapX Orders', () => { it('can swap exact-out trades using uniswapX', () => { // Setup a swap cy.get('#swap-currency-output .token-amount-input').type('300') + cy.wait('@quote') + cy.contains('Try it now').click() // Submit uniswapx order signature @@ -125,6 +149,8 @@ describe('UniswapX Orders', () => { it('renders proper view if uniswapx order expires', () => { // Setup a swap cy.get('#swap-currency-input .token-amount-input').type('300') + cy.wait('@quote') + cy.contains('Try it now').click() // Submit uniswapx order signature @@ -141,6 +167,8 @@ describe('UniswapX Orders', () => { it('renders proper view if uniswapx order has insufficient funds', () => { // Setup a swap cy.get('#swap-currency-input .token-amount-input').type('300') + cy.wait('@quote') + cy.contains('Try it now').click() // Submit uniswapx order signature @@ -157,7 +185,7 @@ describe('UniswapX Orders', () => { describe('UniswapX Eth Input', () => { beforeEach(() => { - cy.intercept(QuoteEndpoint, { fixture: QuoteWithEthInput }) + stubNonPriceQuoteWith(QuoteWithEthInput) cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' }) cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' }) @@ -177,6 +205,8 @@ describe('UniswapX Eth Input', () => { it('can swap using uniswapX with ETH as input', () => { // Setup a swap cy.get('#swap-currency-input .token-amount-input').type('1') + + cy.wait('@quote') cy.contains('Try it now').click() // Prompt ETH wrap to use for order @@ -209,6 +239,8 @@ describe('UniswapX Eth Input', () => { it('switches swap input to WETH after wrap', () => { // Setup a swap cy.get('#swap-currency-input .token-amount-input').type('1') + cy.wait('@quote') + cy.contains('Try it now').click() // Prompt ETH wrap and confirm @@ -344,7 +376,7 @@ describe('UniswapX activity history', () => { // Open activity history cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click() - // Ensure gql and local order have been deduped, such that there is only one swap activity listed + // Ensure gql and local order have been deduped, such that there is one swap activity listed cy.get(getTestSelector('activity-content')).contains('Swapped').should('have.length', 1) }) diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 3c7659f279..df81ac0976 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -74,18 +74,14 @@ Cypress.Commands.overwrite( } ) -Cypress.Commands.add('waitForAmplitudeEvent', (eventName, timeout = 5000 /* 5s */) => { - const startTime = new Date().getTime() - +Cypress.Commands.add('waitForAmplitudeEvent', (eventName) => { function checkRequest() { - return cy.wait('@amplitude', { timeout }).then((interception) => { + return cy.wait('@amplitude').then((interception) => { const events = interception.request.body.events const event = events.find((event: any) => event.event_type === eventName) if (event) { return cy.wrap(event) - } else if (new Date().getTime() - startTime > timeout) { - throw new Error(`Event ${eventName} not found within the specified timeout`) } else { return checkRequest() } diff --git a/cypress/support/setupTests.ts b/cypress/support/setupTests.ts index 7ae34a206d..fac176c6f3 100644 --- a/cypress/support/setupTests.ts +++ b/cypress/support/setupTests.ts @@ -34,6 +34,9 @@ beforeEach(() => { ) }).intercept('https://*.sentry.io', { statusCode: 200 }) + // Mock statsig to allow us to mock flags. + cy.intercept(/statsig/, { statusCode: 409 }) + // Mock our own token list responses to avoid the latency of IPFS. cy.intercept('https://gateway.ipfs.io/ipns/tokens.uniswap.org', TokenListJSON) .intercept('https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org', { statusCode: 404 }) diff --git a/src/components/Logo/AssetLogo.tsx b/src/components/Logo/AssetLogo.tsx index ad8fa1e017..e302cb060e 100644 --- a/src/components/Logo/AssetLogo.tsx +++ b/src/components/Logo/AssetLogo.tsx @@ -79,6 +79,7 @@ export default function AssetLogo({ onLoad={() => void setImgLoaded(true)} onError={nextSrc} imgLoaded={imgLoaded} + loading="lazy" /> ) : ( diff --git a/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap b/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap index 98a8802397..14c274dc6f 100644 --- a/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap +++ b/src/components/SearchModal/__snapshots__/CommonBases.test.tsx.snap @@ -105,6 +105,7 @@ exports[`CommonBases renders without crashing 1`] = ` ETH logo @@ -130,6 +131,7 @@ exports[`CommonBases renders without crashing 1`] = ` DAI logo @@ -155,6 +157,7 @@ exports[`CommonBases renders without crashing 1`] = ` USDC logo @@ -180,6 +183,7 @@ exports[`CommonBases renders without crashing 1`] = ` USDT logo @@ -205,6 +209,7 @@ exports[`CommonBases renders without crashing 1`] = ` WBTC logo @@ -230,6 +235,7 @@ exports[`CommonBases renders without crashing 1`] = ` WETH logo diff --git a/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap b/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap index 0ac8eae691..4daf2a27fc 100644 --- a/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap +++ b/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap @@ -2762,6 +2762,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` ABC logo @@ -2828,6 +2829,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` DEF logo @@ -2846,6 +2848,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` ABC logo @@ -2887,6 +2890,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` DEF logo @@ -4429,6 +4433,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` ABC logo @@ -4495,6 +4500,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` DEF logo @@ -4513,6 +4519,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` ABC logo @@ -4554,6 +4561,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` DEF logo @@ -6096,6 +6104,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` ABC logo @@ -6162,6 +6171,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` GHI logo @@ -6180,6 +6190,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` ABC logo @@ -6221,6 +6232,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` GHI logo @@ -7971,6 +7983,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` ABC logo @@ -8037,6 +8050,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` DEF logo @@ -8055,6 +8069,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` ABC logo @@ -8096,6 +8111,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` DEF logo @@ -9846,6 +9862,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` ABC logo @@ -9912,6 +9929,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` DEF logo @@ -9930,6 +9948,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` ABC logo @@ -9971,6 +9990,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` DEF logo diff --git a/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap b/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap index b0cf7d1fbe..e1d366f260 100644 --- a/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap +++ b/src/components/swap/__snapshots__/SwapModalHeader.test.tsx.snap @@ -165,6 +165,7 @@ exports[`SwapModalHeader.tsx matches base snapshot, test trade exact input 1`] = ABC logo @@ -213,6 +214,7 @@ exports[`SwapModalHeader.tsx matches base snapshot, test trade exact input 1`] = DEF logo @@ -388,6 +390,7 @@ exports[`SwapModalHeader.tsx renders ETH input token for an ETH input UniswapX s ETH logo @@ -436,6 +439,7 @@ exports[`SwapModalHeader.tsx renders ETH input token for an ETH input UniswapX s DEF logo @@ -611,6 +615,7 @@ exports[`SwapModalHeader.tsx renders preview trades with loading states 1`] = ` DEF logo @@ -659,6 +664,7 @@ exports[`SwapModalHeader.tsx renders preview trades with loading states 1`] = ` DEF logo @@ -834,6 +840,7 @@ exports[`SwapModalHeader.tsx test trade exact output, no recipient 1`] = ` ABC logo @@ -882,6 +889,7 @@ exports[`SwapModalHeader.tsx test trade exact output, no recipient 1`] = ` GHI logo diff --git a/src/components/swap/styled.tsx b/src/components/swap/styled.tsx index d8c1dcc7c0..2176852a95 100644 --- a/src/components/swap/styled.tsx +++ b/src/components/swap/styled.tsx @@ -107,14 +107,13 @@ const UniswapXShineInner = styled.div` ` // overflow hidden to hide the SwapMustacheShadow -export const SwapOptInSmallContainer = styled.div<{ visible: boolean; shouldAnimate: boolean }>` +export const SwapOptInSmallContainer = styled.div<{ visible: boolean }>` visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')}; overflow: hidden; margin-top: -14px; transform: translateY(${({ visible }) => (visible ? 0 : -80)}px); transition: all ease 400ms; - animation: ${({ visible, shouldAnimate }) => - !shouldAnimate ? '' : visible ? `spring-down 900ms ease forwards` : 'back-up 200ms ease forwards'}; + animation: ${({ visible }) => (visible ? `spring-down 900ms ease forwards` : 'back-up 200ms ease forwards')}; ${springDownKeyframes} ${backUpKeyframes} diff --git a/src/hooks/useStablecoinPrice.ts b/src/hooks/useStablecoinPrice.ts index 70851a0ef0..695f773d83 100644 --- a/src/hooks/useStablecoinPrice.ts +++ b/src/hooks/useStablecoinPrice.ts @@ -2,7 +2,7 @@ import { ChainId, Currency, CurrencyAmount, Price, Token, TradeType } from '@uni import { useWeb3React } from '@web3-react/core' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { useMemo, useRef } from 'react' -import { INTERNAL_ROUTER_PREFERENCE_PRICE } from 'state/routing/types' +import { ClassicTrade, INTERNAL_ROUTER_PREFERENCE_PRICE } from 'state/routing/types' import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade' import { @@ -55,7 +55,8 @@ export default function useStablecoinPrice(currency?: Currency): Price @@ -4548,6 +4549,7 @@ exports[`disable nft on landing page renders nft information and card 1`] = ` ETH logo diff --git a/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap index 03b49fca7b..8f406f71a0 100644 --- a/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap +++ b/src/pages/PoolDetails/__snapshots__/PoolDetailsStats.test.tsx.snap @@ -255,6 +255,7 @@ exports[`PoolDetailsStats pool balance chart not visible on mobile 1`] = ` UNKNOWN logo @@ -274,6 +275,7 @@ exports[`PoolDetailsStats pool balance chart not visible on mobile 1`] = ` WETH logo diff --git a/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap b/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap index 1aeec655d2..81a57f01ea 100644 --- a/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap +++ b/src/pages/PoolDetails/__snapshots__/index.test.tsx.snap @@ -911,6 +911,7 @@ exports[`PoolDetailsPage pool header is displayed when data is received from the UNKNOWN logo @@ -985,6 +986,7 @@ exports[`PoolDetailsPage pool header is displayed when data is received from the WETH logo diff --git a/src/pages/Swap/UniswapXOptIn.tsx b/src/pages/Swap/UniswapXOptIn.tsx index 0ea44934d4..f75480ad6f 100644 --- a/src/pages/Swap/UniswapXOptIn.tsx +++ b/src/pages/Swap/UniswapXOptIn.tsx @@ -15,7 +15,7 @@ import { UniswapXShine, } from 'components/swap/styled' import { formatCommonPropertiesForTrade } from 'lib/utils/analytics' -import { PropsWithChildren, useEffect, useRef, useState } from 'react' +import { PropsWithChildren, useRef, useState } from 'react' import { X } from 'react-feather' import { useLocation } from 'react-router-dom' import { Text } from 'rebass' @@ -76,16 +76,6 @@ const OptInContents = ({ const isVisible = isOnClassic const location = useLocation() - // adding this as we need to mount and then set shouldAnimate = true after it mounts to avoid a flicker on initial mount - const [shouldAnimate, setShouldAnimate] = useState(false) - - useEffect(() => { - if (!isVisible || shouldAnimate) return - // delay visible animation a bit - const tm = setTimeout(() => setShouldAnimate(true), 350) - return () => clearTimeout(tm) - }, [isVisible, shouldAnimate]) - const tryItNowElement = ( +