test(cypress): clean up types/assumptions/infura (#7046)

This commit is contained in:
Zach Pomerantz 2023-08-01 22:13:19 -07:00 committed by GitHub
parent b4b6c347e3
commit 7978ed97a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 93 additions and 219 deletions

@ -100,6 +100,23 @@ jobs:
path: build path: build
if-no-files-found: error if-no-files-found: error
cypress-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: ./.github/actions/cache-on-main
with:
path: node_modules/.cache
key: ${{ runner.os }}-cypress-tsc-${{ github.run_id }}
restore-keys: ${{ runner.os }}-cypress-tsc-
- run: yarn typecheck:cypress
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Cypress typecheck
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
# Allows for parallel re-runs of cypress tests without re-building. # Allows for parallel re-runs of cypress tests without re-building.
cypress-rerun: cypress-rerun:
runs-on: ubuntu-latest runs-on: ubuntu-latest

@ -1,7 +1,6 @@
import codeCoverageTask from '@cypress/code-coverage/task' import codeCoverageTask from '@cypress/code-coverage/task'
import { defineConfig } from 'cypress' import { defineConfig } from 'cypress'
import { setupHardhatEvents } from 'cypress-hardhat' import { setupHardhatEvents } from 'cypress-hardhat'
import { unlinkSync } from 'fs'
export default defineConfig({ export default defineConfig({
projectId: 'yp82ef', projectId: 'yp82ef',
@ -9,22 +8,11 @@ export default defineConfig({
chromeWebSecurity: false, chromeWebSecurity: false,
experimentalMemoryManagement: true, // better memory management, see https://github.com/cypress-io/cypress/pull/25462 experimentalMemoryManagement: true, // better memory management, see https://github.com/cypress-io/cypress/pull/25462
retries: { runMode: 2 }, retries: { runMode: 2 },
videoCompression: false, video: false, // GH provides 2 CPUs, and cypress video eats one up, see https://github.com/cypress-io/cypress/issues/20468#issuecomment-1307608025
e2e: { e2e: {
async setupNodeEvents(on, config) { async setupNodeEvents(on, config) {
await setupHardhatEvents(on, config) await setupHardhatEvents(on, config)
codeCoverageTask(on, config) codeCoverageTask(on, config)
// Delete recorded videos for specs that passed without flakes.
on('after:spec', async (spec, results) => {
if (results && results.video) {
// If there were no failures (including flakes), delete the recorded video.
if (!results.tests?.some((test) => test.attempts.some((attempt) => attempt?.state === 'failed'))) {
unlinkSync(results.video)
}
}
})
return config return config
}, },
baseUrl: 'http://localhost:3000', baseUrl: 'http://localhost:3000',

@ -52,7 +52,7 @@ This becomes more relevant as you work with data on the blockchain, as you'll ne
``` ```
cy.hardhat().then(async (hardhat) => { cy.hardhat().then(async (hardhat) => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`, { ethereum: 'hardhat' }) cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1') cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
cy.get('#swap-button').click() cy.get('#swap-button').click()
cy.contains('Confirm swap').click() cy.contains('Confirm swap').click()
@ -68,7 +68,7 @@ cy.hardhat().then(async (hardhat) => {
``` ```
``` ```
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`, { ethereum: 'hardhat' }) cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1') cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
cy.get('#swap-button').click() cy.get('#swap-button').click()
cy.contains('Confirm swap').click() cy.contains('Confirm swap').click()
@ -87,7 +87,6 @@ cy.hardhat().then(async (hardhat) => {
### Working with the blockchain (ie hardhat) ### Working with the blockchain (ie hardhat)
Our tests use a local hardhat node to simulate blockchain transactions. This can be accessed with `cy.hardhat().then((hardhat) => ...)`. Our tests use a local hardhat node to simulate blockchain transactions. This can be accessed with `cy.hardhat().then((hardhat) => ...)`.
Currently, tests using hardhat must opt-in in when they load the page: `cy.visit('/swap', { ethereum: 'hardhat' })`. This will not be necessary once we've totally migrated to hardhat.
By default, automining is turned on, so that any transaction that you send to the blockchain is mined immediately. If you want to assert on intermediate states (between sending a transaction and mining it), you can turn off automining: `cy.hardhat({ automine: false })`. By default, automining is turned on, so that any transaction that you send to the blockchain is mined immediately. If you want to assert on intermediate states (between sending a transaction and mining it), you can turn off automining: `cy.hardhat({ automine: false })`.

@ -9,36 +9,29 @@ describe('Add Liquidity', () => {
}) })
}) })
it('loads the two correct tokens', () => { it('loads the token pair', () => {
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/500') cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH/500')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI') cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH') cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
cy.contains('0.05% fee tier')
}) })
it('does not crash if ETH is duplicated', () => { it('does not crash if token is duplicated', () => {
cy.visit('/add/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6') cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'ETH') cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH') cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'UNI')
}) })
it.skip('token not in storage is loaded', () => { it('single token can be selected', () => {
cy.visit('/add/0x07865c6e87b9f70255377e024ace6630c1eaa37f/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'USDC')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'UNI')
})
it.skip('single token can be selected', () => {
cy.visit('/add/0x07865c6e87b9f70255377e024ace6630c1eaa37f')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'USDC')
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984') cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI') cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
}) })
it.skip('loads fee tier distribution', () => { it('loads fee tier distribution', () => {
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => { cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => { cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
if (hasQuery(req, 'FeeTierDistributionQuery')) { if (hasQuery(req, 'FeeTierDistribution')) {
req.alias = 'FeeTierDistributionQuery' req.alias = 'FeeTierDistribution'
req.reply({ req.reply({
body: { body: {
@ -53,12 +46,10 @@ describe('Add Liquidity', () => {
} }
}) })
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6') cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH')
cy.wait('@FeeTierDistribution')
cy.wait('@FeeTierDistributionQuery')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier') cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40%') cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40% select')
}) })
}) })
}) })

@ -3,7 +3,7 @@ import { getTestSelector } from '../../utils'
describe('Mini Portfolio account drawer', () => { describe('Mini Portfolio account drawer', () => {
beforeEach(() => { beforeEach(() => {
cy.intercept(/api.uniswap.org\/v1\/graphql/, cy.spy().as('gqlSpy')) cy.intercept(/api.uniswap.org\/v1\/graphql/, cy.spy().as('gqlSpy'))
cy.visit('/swap', { ethereum: 'hardhat' }) cy.visit('/swap')
}) })
it('fetches balances when account button is first hovered', () => { it('fetches balances when account button is first hovered', () => {

@ -94,9 +94,7 @@ describe('mini-portfolio activity history', () => {
}) })
it('should deduplicate activity history by nonce', () => { it('should deduplicate activity history by nonce', () => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`, { ethereum: 'hardhat' }).hardhat({ cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`).hardhat({ automine: false })
automine: false,
})
// Input swap info. // Input swap info.
cy.get('#swap-currency-input .token-amount-input').clear().type('1').should('have.value', '1') cy.get('#swap-currency-input .token-amount-input').clear().type('1').should('have.value', '1')

@ -52,7 +52,7 @@ describe('Testing nfts', () => {
cy.visit('/') cy.visit('/')
cy.get(getTestSelector('web3-status-connected')).click() cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('mini-portfolio-navbar')).contains('NFTs').click() cy.get(getTestSelector('mini-portfolio-navbar')).contains('NFTs').click()
cy.get(getTestSelector('mini-portfolio-nft')).click() cy.get(getTestSelector('mini-portfolio-nft')).first().click()
cy.get(getTestSelector('mini-portfolio-navbar')).should('not.be.visible') cy.get(getTestSelector('mini-portfolio-navbar')).should('not.be.visible')
}) })
}) })

@ -17,9 +17,7 @@ function initiateSwap() {
describe('Permit2', () => { describe('Permit2', () => {
function setupInputs(inputToken: Token, outputToken: Token) { function setupInputs(inputToken: Token, outputToken: Token) {
// Sets up a swap between inputToken and outputToken. // Sets up a swap between inputToken and outputToken.
cy.visit(`/swap/?inputCurrency=${inputToken.address}&outputCurrency=${outputToken.address}`, { cy.visit(`/swap/?inputCurrency=${inputToken.address}&outputCurrency=${outputToken.address}`)
ethereum: 'hardhat',
})
cy.get('#swap-currency-input .token-amount-input').type('0.01') cy.get('#swap-currency-input .token-amount-input').type('0.01')
} }

@ -1,25 +1,7 @@
describe('Remove Liquidity', () => { describe('Remove Liquidity', () => {
it('eth remove', () => { it('loads the token pair', () => {
cy.visit('/remove/v2/ETH/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984') cy.visit('/remove/v2/ETH/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH') cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI') cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI')
}) })
it('eth remove swap order', () => {
cy.visit('/remove/v2/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'UNI')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
})
it('loads the two correct tokens', () => {
cy.visit('/remove/v2/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI')
})
it('does not crash if ETH is duplicated', () => {
cy.visit('/remove/v2/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
})
}) })

@ -7,7 +7,7 @@ import { getBalance, getTestSelector } from '../../utils'
describe('Swap errors', () => { describe('Swap errors', () => {
it('wallet rejection', () => { it('wallet rejection', () => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`, { ethereum: 'hardhat' }) cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
cy.hardhat().then((hardhat) => { cy.hardhat().then((hardhat) => {
// Stub the wallet to reject any transaction. // Stub the wallet to reject any transaction.
cy.stub(hardhat.wallet, 'sendTransaction').log(false).rejects(new Error('user cancelled')) cy.stub(hardhat.wallet, 'sendTransaction').log(false).rejects(new Error('user cancelled'))
@ -28,7 +28,7 @@ describe('Swap errors', () => {
}) })
it('transaction past deadline', () => { it('transaction past deadline', () => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`, { ethereum: 'hardhat' }) cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
cy.hardhat({ automine: false }) cy.hardhat({ automine: false })
getBalance(USDC_MAINNET).then((initialBalance) => { getBalance(USDC_MAINNET).then((initialBalance) => {
// Enter amount to swap // Enter amount to swap
@ -63,7 +63,7 @@ describe('Swap errors', () => {
}) })
it('slippage failure', () => { it('slippage failure', () => {
cy.visit(`/swap?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`, { ethereum: 'hardhat' }) cy.visit(`/swap?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
cy.hardhat({ automine: false }).then(async (hardhat) => { cy.hardhat({ automine: false }).then(async (hardhat) => {
await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDC_MAINNET, 500e6)) await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDC_MAINNET, 500e6))
await hardhat.mine() await hardhat.mine()
@ -113,9 +113,7 @@ describe('Swap errors', () => {
cy.get(getTestSelector('web3-status-connected')).should('not.contain', 'Pending') cy.get(getTestSelector('web3-status-connected')).should('not.contain', 'Pending')
cy.get(getTestSelector('popups')).contains('Swapped') cy.get(getTestSelector('popups')).contains('Swapped')
cy.get(getTestSelector('popups')).contains('Swap failed') cy.get(getTestSelector('popups')).contains('Swap failed')
getBalance(DAI).then((currentDaiBalance) => { getBalance(DAI).should('be.closeTo', initialBalance + 200, 1)
expect(currentDaiBalance).to.be.closeTo(initialBalance + 200, 1)
})
}) })
}) })
}) })

@ -3,7 +3,7 @@ import { getTestSelector } from '../../utils'
describe('Swap settings', () => { describe('Swap settings', () => {
it('Opens and closes the settings menu', () => { it('Opens and closes the settings menu', () => {
cy.visit('/swap', { featureFlags: [FeatureFlag.uniswapXEnabled], ethereum: 'hardhat' }) cy.visit('/swap', { featureFlags: [FeatureFlag.uniswapXEnabled] })
cy.contains('Settings').should('not.exist') cy.contains('Settings').should('not.exist')
cy.get(getTestSelector('open-settings-dialog-button')).click() cy.get(getTestSelector('open-settings-dialog-button')).click()
cy.contains('Max slippage').should('exist') cy.contains('Max slippage').should('exist')

@ -52,7 +52,7 @@ describe('Swap', () => {
}) })
it('swaps ETH for USDC', () => { it('swaps ETH for USDC', () => {
cy.visit('/swap', { ethereum: 'hardhat' }) cy.visit('/swap')
cy.hardhat({ automine: false }) cy.hardhat({ automine: false })
getBalance(USDC_MAINNET).then((initialBalance) => { getBalance(USDC_MAINNET).then((initialBalance) => {
// Select USDC // Select USDC

@ -6,9 +6,7 @@ const WETH = WETH9[ChainId.MAINNET]
describe('Swap wrap', () => { describe('Swap wrap', () => {
beforeEach(() => { beforeEach(() => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${WETH.address}`, { ethereum: 'hardhat' }).hardhat({ cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${WETH.address}`).hardhat({ automine: false })
automine: false,
})
}) })
it('ETH to wETH is same value (wrapped swaps have no price impact)', () => { it('ETH to wETH is same value (wrapped swaps have no price impact)', () => {

@ -93,9 +93,7 @@ describe('Token details', () => {
beforeEach(() => { beforeEach(() => {
// On mobile widths, we just link back to /swap instead of rendering the swap component. // On mobile widths, we just link back to /swap instead of rendering the swap component.
cy.viewport(1200, 800) cy.viewport(1200, 800)
cy.visit(`/tokens/ethereum/${UNI_MAINNET.address}`, { cy.visit(`/tokens/ethereum/${UNI_MAINNET.address}`).then(() => {
ethereum: 'hardhat',
}).then(() => {
cy.wait('@eth_blockNumber') cy.wait('@eth_blockNumber')
cy.scrollTo('top') cy.scrollTo('top')
}) })
@ -145,7 +143,7 @@ describe('Token details', () => {
}) })
it('should show a L2 token even if the user is connected to a different network', () => { it('should show a L2 token even if the user is connected to a different network', () => {
cy.visit('/tokens', { ethereum: 'hardhat' }) cy.visit('/tokens')
cy.get(getTestSelector('tokens-network-filter-selected')).click() cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click() cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum') cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum')

@ -36,7 +36,7 @@ describe('Token explore', () => {
.then(function ($elem) { .then(function ($elem) {
cy.wrap($elem.text()).as('yearlyEthVol') cy.wrap($elem.text()).as('yearlyEthVol')
}) })
expect(cy.get('@dailyEthVol')).to.not.equal(cy.get('@yearlyEthVol')) cy.get('@dailyEthVol').should('not.equal', cy.get('@yearlyEthVol'))
}) })
it('should navigate to token detail page when row clicked', () => { it('should navigate to token detail page when row clicked', () => {

@ -2,8 +2,8 @@ import { getTestSelector } from '../../utils'
import { DISCONNECTED_WALLET_USER_STATE } from '../../utils/user-state' import { DISCONNECTED_WALLET_USER_STATE } from '../../utils/user-state'
describe('disconnect wallet', () => { describe('disconnect wallet', () => {
it('should not clear state', () => { it('should clear state', () => {
cy.visit('/swap', { ethereum: 'hardhat' }) cy.visit('/swap')
cy.get('#swap-currency-input .token-amount-input').clear().type('1') cy.get('#swap-currency-input .token-amount-input').clear().type('1')
// Verify wallet is connected // Verify wallet is connected
@ -28,7 +28,7 @@ describe('disconnect wallet', () => {
describe('connect wallet', () => { describe('connect wallet', () => {
it('should load state', () => { it('should load state', () => {
cy.visit('/swap', { ethereum: 'hardhat', userState: DISCONNECTED_WALLET_USER_STATE }) cy.visit('/swap', { userState: DISCONNECTED_WALLET_USER_STATE })
// Connect the wallet // Connect the wallet
cy.get(getTestSelector('navbar-connect-wallet')).contains('Connect').click() cy.get(getTestSelector('navbar-connect-wallet')).contains('Connect').click()

@ -12,7 +12,7 @@ function switchChain(chain: string) {
describe('network switching', () => { describe('network switching', () => {
beforeEach(() => { beforeEach(() => {
cy.visit('/swap', { ethereum: 'hardhat' }) cy.visit('/swap')
cy.get(getTestSelector('web3-status-connected')) cy.get(getTestSelector('web3-status-connected'))
}) })
@ -123,14 +123,14 @@ describe('network switching', () => {
describe('network switching from URL param', () => { describe('network switching from URL param', () => {
it('should switch network from URL param', () => { it('should switch network from URL param', () => {
cy.visit('/swap?chain=polygon', { ethereum: 'hardhat' }) cy.visit('/swap?chain=polygon')
cy.get(getTestSelector('web3-status-connected')) cy.get(getTestSelector('web3-status-connected'))
cy.wait('@wallet_switchEthereumChain') cy.wait('@wallet_switchEthereumChain')
waitsForActiveChain('Polygon') waitsForActiveChain('Polygon')
}) })
it('should be able to switch network after loading from URL param', () => { it('should be able to switch network after loading from URL param', () => {
cy.visit('/swap?chain=polygon', { ethereum: 'hardhat' }) cy.visit('/swap?chain=polygon')
cy.get(getTestSelector('web3-status-connected')) cy.get(getTestSelector('web3-status-connected'))
cy.wait('@wallet_switchEthereumChain') cy.wait('@wallet_switchEthereumChain')
waitsForActiveChain('Polygon') waitsForActiveChain('Polygon')

@ -5,7 +5,6 @@ import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
import { FeatureFlag } from '../../src/featureFlags' import { FeatureFlag } from '../../src/featureFlags'
import { UserState } from '../../src/state/user/reducer' import { UserState } from '../../src/state/user/reducer'
import { CONNECTED_WALLET_USER_STATE } from '../utils/user-state' import { CONNECTED_WALLET_USER_STATE } from '../utils/user-state'
import { injected } from './ethereum'
declare global { declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
@ -16,12 +15,6 @@ declare global {
interface VisitOptions { interface VisitOptions {
serviceWorker?: true serviceWorker?: true
featureFlags?: Array<FeatureFlag> featureFlags?: Array<FeatureFlag>
/**
* The mock ethereum provider to inject into the page.
* @default 'goerli'
*/
// TODO(INFRA-175): Migrate all usage of 'goerli' to 'hardhat'.
ethereum?: 'goerli' | 'hardhat'
/** /**
* Initial user state. * Initial user state.
* @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE} * @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE}
@ -39,8 +32,7 @@ Cypress.Commands.overwrite(
if (typeof url !== 'string') throw new Error('Invalid arguments. The first argument to cy.visit must be the path.') if (typeof url !== 'string') throw new Error('Invalid arguments. The first argument to cy.visit must be the path.')
// Add a hash in the URL if it is not present (to use hash-based routing correctly with queryParams). // Add a hash in the URL if it is not present (to use hash-based routing correctly with queryParams).
let hashUrl = url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url const hashUrl = url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url
if (options?.ethereum === 'goerli') hashUrl += `${url.includes('?') ? '&' : '?'}chain=goerli`
return cy return cy
.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 }) .intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 })
@ -68,11 +60,7 @@ Cypress.Commands.overwrite(
} }
// Inject the mock ethereum provider. // Inject the mock ethereum provider.
if (options?.ethereum === 'hardhat') {
win.ethereum = provider win.ethereum = provider
} else {
win.ethereum = injected
}
}, },
}) })
) )

@ -1,72 +0,0 @@
/**
* Updates cy.visit() to include an injected window.ethereum provider.
*/
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
import { JsonRpcProvider } from '@ethersproject/providers'
import { Wallet } from '@ethersproject/wallet'
import { ChainId } from '@uniswap/sdk-core'
// todo: figure out how env vars actually work in CI
// const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
const TEST_PRIVATE_KEY = '0xe580410d7c37d26c6ad1a837bbae46bc27f9066a466fb3a66e770523b4666d19'
// address of the above key
const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
const CHAIN_ID = ChainId.GOERLI
const HEXLIFIED_CHAIN_ID = `0x${CHAIN_ID.toString(16)}`
const provider = new JsonRpcProvider('https://goerli.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 5)
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
export const injected = new (class extends Eip1193Bridge {
chainId = CHAIN_ID
async sendAsync(...args: any[]) {
console.debug('sendAsync called', ...args)
return this.send(...args)
}
async send(...args: any[]) {
console.debug('send called', ...args)
const isCallbackForm = typeof args[0] === 'object' && typeof args[1] === 'function'
let callback
let method
let params
if (isCallbackForm) {
callback = args[1]
method = args[0].method
params = args[0].params
} else {
method = args[0]
params = args[1]
}
if (method === 'eth_requestAccounts' || method === 'eth_accounts') {
if (isCallbackForm) {
callback({ result: [TEST_ADDRESS_NEVER_USE] })
} else {
return Promise.resolve([TEST_ADDRESS_NEVER_USE])
}
}
if (method === 'eth_chainId') {
if (isCallbackForm) {
callback(null, { result: HEXLIFIED_CHAIN_ID })
} else {
return Promise.resolve(HEXLIFIED_CHAIN_ID)
}
}
try {
const result = await super.send(method, params)
console.debug('result received', method, params, result)
if (isCallbackForm) {
callback(null, { result })
} else {
return result
}
} catch (error) {
if (isCallbackForm) {
callback(error, null)
} else {
throw error
}
}
}
})(signer, provider)

@ -9,13 +9,8 @@ beforeEach(() => {
req.headers['origin'] = 'https://app.uniswap.org' req.headers['origin'] = 'https://app.uniswap.org'
}) })
// Infura uses a test endpoint, which allow-lists http://localhost:3000 instead. // Infura is disabled for cypress tests - calls should be routed through the connected wallet instead.
cy.intercept(/infura.io/, (req) => { cy.intercept(/infura.io/, { statusCode: 404 })
req.headers['referer'] = 'http://localhost:3000'
req.headers['origin'] = 'http://localhost:3000'
req.alias = req.body.method
req.continue()
})
// Log requests to hardhat. // Log requests to hardhat.
cy.intercept(/:8545/, logJsonRpc) cy.intercept(/:8545/, logJsonRpc)
@ -49,7 +44,7 @@ function logJsonRpc(req: CyHttpMessages.IncomingHttpRequest) {
const log = Cypress.log({ const log = Cypress.log({
autoEnd: false, autoEnd: false,
name: req.body.method, name: req.body.method,
message: req.body.params?.map((param: unknown) => message: req.body.params?.map((param: any) =>
typeof param === 'object' ? '{...}' : param?.toString().substring(0, 10) typeof param === 'object' ? '{...}' : param?.toString().substring(0, 10)
), ),
}) })

@ -1,18 +1,13 @@
{ {
"extends": "../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"esModuleInterop": true, "composite": false,
"incremental": true, "incremental": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"], "isolatedModules": false,
"noEmit": true, "noImplicitAny": false,
"strict": true,
"target": "ES5", "target": "ES5",
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/cypress", // avoid clobbering the build tsbuildinfo "tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/cypress", // avoid clobbering the build tsbuildinfo
"types": ["cypress", "node"], "types": ["cypress", "node"],
"jsx": "react"
}, },
"exclude": ["node_modules"],
"include": ["**/*.ts"], "include": ["**/*.ts"],
"watchOptions": {
"excludeDirectories": ["node_modules"]
}
} }

@ -1,5 +1,6 @@
import { ConnectionType } from '../../src/connection/types'
import { UserState } from '../../src/state/user/reducer' import { UserState } from '../../src/state/user/reducer'
export const CONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: 'INJECTED' } export const CONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: ConnectionType.INJECTED }
export const DISCONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: undefined } export const DISCONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: undefined }

@ -27,6 +27,7 @@
"lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .", "lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .",
"typecheck": "tsc", "typecheck": "tsc",
"typecheck:cloud": "tsc -p functions/tsconfig.json", "typecheck:cloud": "tsc -p functions/tsconfig.json",
"typecheck:cypress": "tsc -p cypress/tsconfig.json",
"test": "craco test", "test": "craco test",
"test:cloud": "NODE_OPTIONS=--experimental-vm-modules yarn jest functions --config=functions/jest.config.json", "test:cloud": "NODE_OPTIONS=--experimental-vm-modules yarn jest functions --config=functions/jest.config.json",
"cypress:open": "cypress open --browser chrome --e2e", "cypress:open": "cypress open --browser chrome --e2e",

@ -1,5 +1,5 @@
import store from 'state' import store from 'state'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { updateUserRouterPreference } from 'state/user/reducer' import { updateUserRouterPreference } from 'state/user/reducer'
import { fireEvent, render, screen } from 'test-utils/render' import { fireEvent, render, screen } from 'test-utils/render'

@ -7,7 +7,7 @@ import Toggle from 'components/Toggle'
import { isUniswapXSupportedChain } from 'constants/chains' import { isUniswapXSupportedChain } from 'constants/chains'
import { useUniswapXEnabled } from 'featureFlags/flags/uniswapx' import { useUniswapXEnabled } from 'featureFlags/flags/uniswapx'
import { useAppDispatch } from 'state/hooks' import { useAppDispatch } from 'state/hooks'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { useRouterPreference } from 'state/user/hooks' import { useRouterPreference } from 'state/user/hooks'
import { updateDisabledUniswapX } from 'state/user/reducer' import { updateDisabledUniswapX } from 'state/user/reducer'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'

@ -11,7 +11,7 @@ import useTransactionDeadline from 'hooks/useTransactionDeadline'
import useNativeCurrency from 'lib/hooks/useNativeCurrency' import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { AlertTriangle } from 'react-feather' import { AlertTriangle } from 'react-feather'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { InterfaceTrade } from 'state/routing/types' import { InterfaceTrade } from 'state/routing/types'
import { getTransactionCount, isClassicTrade } from 'state/routing/utils' import { getTransactionCount, isClassicTrade } from 'state/routing/utils'
import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks' import { useRouterPreference, useUserSlippageTolerance } from 'state/user/hooks'

@ -1,7 +1,7 @@
import { renderHook } from '@testing-library/react' import { renderHook } from '@testing-library/react'
import { CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { DAI, USDC_MAINNET } from 'constants/tokens' import { DAI, USDC_MAINNET } from 'constants/tokens'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { TradeState } from 'state/routing/types' import { TradeState } from 'state/routing/types'
import { useRouterPreference } from 'state/user/hooks' import { useRouterPreference } from 'state/user/hooks'
import { mocked } from 'test-utils/mocked' import { mocked } from 'test-utils/mocked'

@ -3,7 +3,7 @@ import { useWeb3React } from '@web3-react/core'
import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
import { DebounceSwapQuoteVariant, useDebounceSwapQuoteFlag } from 'featureFlags/flags/debounceSwapQuote' import { DebounceSwapQuoteVariant, useDebounceSwapQuoteFlag } from 'featureFlags/flags/debounceSwapQuote'
import { useMemo } from 'react' import { useMemo } from 'react'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { ClassicTrade, InterfaceTrade, QuoteMethod, TradeState } from 'state/routing/types' import { ClassicTrade, InterfaceTrade, QuoteMethod, TradeState } from 'state/routing/types'
import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade' import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade'
import { useRouterPreference } from 'state/user/hooks' import { useRouterPreference } from 'state/user/hooks'

@ -4,7 +4,8 @@ import { useRoutingAPIForPrice } from 'featureFlags/flags/priceRoutingApi'
import { useUniswapXEnabled } from 'featureFlags/flags/uniswapx' import { useUniswapXEnabled } from 'featureFlags/flags/uniswapx'
import { useUniswapXSyntheticQuoteEnabled } from 'featureFlags/flags/uniswapXUseSyntheticQuote' import { useUniswapXSyntheticQuoteEnabled } from 'featureFlags/flags/uniswapXUseSyntheticQuote'
import { useMemo } from 'react' import { useMemo } from 'react'
import { GetQuoteArgs, INTERNAL_ROUTER_PREFERENCE_PRICE, RouterPreference } from 'state/routing/slice' import { GetQuoteArgs, INTERNAL_ROUTER_PREFERENCE_PRICE } from 'state/routing/slice'
import { RouterPreference } from 'state/routing/types'
import { currencyAddressForSwapQuote } from 'state/routing/utils' import { currencyAddressForSwapQuote } from 'state/routing/utils'
import { useUserDisabledUniswapX } from 'state/user/hooks' import { useUserDisabledUniswapX } from 'state/user/hooks'

@ -2,7 +2,7 @@ import { Currency, CurrencyAmount, NativeCurrency, Percent, Token, TradeType } f
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance' import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useBestTrade } from 'hooks/useBestTrade' import { useBestTrade } from 'hooks/useBestTrade'
import { useMemo } from 'react' import { useMemo } from 'react'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { ClassicTrade, TradeState } from 'state/routing/types' import { ClassicTrade, TradeState } from 'state/routing/types'
import { isClassicTrade } from 'state/routing/utils' import { isClassicTrade } from 'state/routing/utils'

@ -19,7 +19,7 @@ import { PropsWithChildren, useEffect, useRef, useState } from 'react'
import { X } from 'react-feather' import { X } from 'react-feather'
import { Text } from 'rebass' import { Text } from 'rebass'
import { useAppDispatch } from 'state/hooks' import { useAppDispatch } from 'state/hooks'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { isClassicTrade } from 'state/routing/utils' import { isClassicTrade } from 'state/routing/utils'
import { SwapInfo } from 'state/swap/hooks' import { SwapInfo } from 'state/swap/hooks'
import { useRouterPreference, useUserDisabledUniswapX } from 'state/user/hooks' import { useRouterPreference, useUserDisabledUniswapX } from 'state/user/hooks'

@ -9,6 +9,7 @@ import { trace } from 'tracing/trace'
import { import {
QuoteMethod, QuoteMethod,
QuoteState, QuoteState,
RouterPreference,
RoutingConfig, RoutingConfig,
SwapRouterNativeAssets, SwapRouterNativeAssets,
TradeResult, TradeResult,
@ -22,12 +23,6 @@ if (UNISWAP_API_URL === undefined) {
throw new Error(`UNISWAP_API_URL must be a defined environment variable`) throw new Error(`UNISWAP_API_URL must be a defined environment variable`)
} }
export enum RouterPreference {
X = 'uniswapx',
API = 'api',
CLIENT = 'client',
}
// This is excluded from `RouterPreference` enum because it's only used // This is excluded from `RouterPreference` enum because it's only used
// internally for token -> USDC trades to get a USD value. // internally for token -> USDC trades to get a USD value.
export const INTERNAL_ROUTER_PREFERENCE_PRICE = 'price' as const export const INTERNAL_ROUTER_PREFERENCE_PRICE = 'price' as const

@ -18,6 +18,12 @@ export enum QuoteMethod {
CLIENT_SIDE_FALLBACK = 'CLIENT_SIDE_FALLBACK', // If client-side was used after the routing-api call failed. CLIENT_SIDE_FALLBACK = 'CLIENT_SIDE_FALLBACK', // If client-side was used after the routing-api call failed.
} }
export enum RouterPreference {
X = 'uniswapx',
API = 'api',
CLIENT = 'client',
}
// from https://github.com/Uniswap/routing-api/blob/main/lib/handlers/schema.ts // from https://github.com/Uniswap/routing-api/blob/main/lib/handlers/schema.ts
type TokenInRoute = Pick<Token, 'address' | 'chainId' | 'symbol' | 'decimals'> type TokenInRoute = Pick<Token, 'address' | 'chainId' | 'symbol' | 'decimals'>

@ -6,10 +6,10 @@ import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo'
import { useRoutingAPIArguments } from 'lib/hooks/routing/useRoutingAPIArguments' import { useRoutingAPIArguments } from 'lib/hooks/routing/useRoutingAPIArguments'
import ms from 'ms.macro' import ms from 'ms.macro'
import { useMemo } from 'react' import { useMemo } from 'react'
import { INTERNAL_ROUTER_PREFERENCE_PRICE, RouterPreference } from 'state/routing/slice' import { INTERNAL_ROUTER_PREFERENCE_PRICE } from 'state/routing/slice'
import { useGetQuoteQuery } from 'state/routing/slice' import { useGetQuoteQuery } from 'state/routing/slice'
import { ClassicTrade, InterfaceTrade, QuoteMethod, QuoteState, TradeState } from './types' import { ClassicTrade, InterfaceTrade, QuoteMethod, QuoteState, RouterPreference, TradeState } from './types'
const TRADE_NOT_FOUND = { state: TradeState.NO_ROUTE_FOUND, trade: undefined } as const const TRADE_NOT_FOUND = { state: TradeState.NO_ROUTE_FOUND, trade: undefined } as const
const TRADE_LOADING = { state: TradeState.LOADING, trade: undefined } as const const TRADE_LOADING = { state: TradeState.LOADING, trade: undefined } as const

@ -11,7 +11,7 @@ import { isAvalanche, isBsc, isMatic, nativeOnChain } from 'constants/tokens'
import { toSlippagePercent } from 'utils/slippage' import { toSlippagePercent } from 'utils/slippage'
import { getApproveInfo, getWrapInfo } from './gas' import { getApproveInfo, getWrapInfo } from './gas'
import { GetQuoteArgs, INTERNAL_ROUTER_PREFERENCE_PRICE, RouterPreference } from './slice' import { GetQuoteArgs, INTERNAL_ROUTER_PREFERENCE_PRICE } from './slice'
import { import {
ClassicQuoteData, ClassicQuoteData,
ClassicTrade, ClassicTrade,
@ -21,6 +21,7 @@ import {
PoolType, PoolType,
QuoteMethod, QuoteMethod,
QuoteState, QuoteState,
RouterPreference,
SwapRouterNativeAssets, SwapRouterNativeAssets,
TradeFillType, TradeFillType,
TradeResult, TradeResult,

@ -2,7 +2,7 @@ import { act } from '@testing-library/react'
import { Percent } from '@uniswap/sdk-core' import { Percent } from '@uniswap/sdk-core'
import { USDC_MAINNET } from 'constants/tokens' import { USDC_MAINNET } from 'constants/tokens'
import store from 'state' import store from 'state'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { renderHook } from 'test-utils/render' import { renderHook } from 'test-utils/render'
import { import {

@ -7,7 +7,7 @@ import { L2_DEADLINE_FROM_NOW } from 'constants/misc'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { useAppDispatch, useAppSelector } from 'state/hooks' import { useAppDispatch, useAppSelector } from 'state/hooks'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { UserAddedToken } from 'types/tokens' import { UserAddedToken } from 'types/tokens'
import { BASES_TO_TRACK_LIQUIDITY_FOR, PINNED_PAIRS } from '../../constants/routing' import { BASES_TO_TRACK_LIQUIDITY_FOR, PINNED_PAIRS } from '../../constants/routing'

@ -1,5 +1,5 @@
import { createStore, Store } from 'redux' import { createStore, Store } from 'redux'
import { RouterPreference } from 'state/routing/slice' import { RouterPreference } from 'state/routing/types'
import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants/misc' import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants/misc'
import { updateVersion } from '../global/actions' import { updateVersion } from '../global/actions'

@ -1,9 +1,9 @@
import { createSlice } from '@reduxjs/toolkit' import { createSlice } from '@reduxjs/toolkit'
import { ConnectionType } from 'connection/types'
import { SupportedLocale } from 'constants/locales'
import { RouterPreference } from 'state/routing/slice'
import { ConnectionType } from '../../connection/types'
import { SupportedLocale } from '../../constants/locales'
import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants/misc' import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants/misc'
import { RouterPreference } from '../../state/routing/types'
import { updateVersion } from '../global/actions' import { updateVersion } from '../global/actions'
import { SerializedPair, SerializedToken, SlippageTolerance } from './types' import { SerializedPair, SerializedToken, SlippageTolerance } from './types'

@ -30,9 +30,5 @@
"types": ["jest"], "types": ["jest"],
"useUnknownInCatchVariables": false "useUnknownInCatchVariables": false
}, },
"exclude": ["node_modules"],
"include": ["src/**/*", "src/**/*.json"], "include": ["src/**/*", "src/**/*.json"],
"watchOptions": {
"excludeDirectories": ["node_modules"]
}
} }