Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .env
Original file line number Diff line number Diff line change
@@ -1,8 +0,0 @@
#######################################
# End To End Test
#######################################
# ⚠️ Required for E2E tests to run

# Cypress Integration Tests
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY=
CYPRESS_INTEGRATION_TESTS_ALCHEMY_KEY=
13 changes: 12 additions & 1 deletion .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,15 @@

# Enable Telegram development bypass mode
# When enabled, Telegram verification is bypassed and uses a mock user (@devuser)
REACT_APP_TG_DEV_BYPASS=true
REACT_APP_TG_DEV_BYPASS=true


#######################################
# End To End Test
#######################################
# ⚠️ Required for E2E tests to run

# Cypress Integration Tests
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY=
CYPRESS_INTEGRATION_TESTS_ALCHEMY_KEY=
CYPRESS_INTEGRATION_TESTS_INFURA_KEY=
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ jobs:
env:
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
CYPRESS_INTEGRATION_TESTS_ALCHEMY_KEY: ${{ secrets.CYPRESS_INTEGRATION_TESTS_ALCHEMY_KEY }}
CYPRESS_INTEGRATION_TESTS_INFURA_KEY: ${{ secrets.CYPRESS_INTEGRATION_TESTS_INFURA_KEY }}

# - uses: actions/upload-artifact@v3
# if: always()
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ yarn run cosmos
> your root `.env.local` file with:
>
> - `CYPRESS_INTEGRATION_TEST_PRIVATE_KEY=<your-private-key>`: Private key
> - `CYPRESS_INTEGRATION_TESTS_ALCHEMY_KEY=<your-alchemy-key>`: Alchemy key
> - `CYPRESS_INTEGRATION_TESTS_INFURA_KEY=<your-infura-key>`: Infura key
> - `CYPRESS_INTEGRATION_TESTS_ALCHEMY_KEY=<your-alchemy-key>`: Alchemy key (preferred if both are set)

To launch it with our development server (so you have live-reloading):

Expand Down Expand Up @@ -171,7 +172,7 @@ REACT_APP_NETWORK_URL_43114: https://...
Additionally, if you plan to run the integration tests locally you must define:

```ini
INTEGRATION_TESTS_ALCHEMY_KEY: YOUR_ALCHEMY_KEY
INTEGRATION_TESTS_INFURA_KEY: YOUR_INFURA_KEY
INTEGRATION_TESTS_PRIVATE_KEY: YOUR_TEST_WALLET_PRIVATE_KEY
```

Expand Down
4 changes: 2 additions & 2 deletions apps/cowswap-frontend-e2e/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default defineConfig({
supportFile: 'src/support/index.ts',
video: false,
screenshotOnRunFailure: false,
defaultCommandTimeout: 5_000,
pageLoadTimeout: 15_000,
defaultCommandTimeout: 25_000,
pageLoadTimeout: 25_000,
},
})
2 changes: 2 additions & 0 deletions apps/cowswap-frontend-e2e/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const SMALL_TIMEOUT = 10_000
export const LARGE_TIMEOUT = 20_000
7 changes: 6 additions & 1 deletion apps/cowswap-frontend-e2e/src/e2e/fiat-amounts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ function parseFiatAmountText(text: string): number {

describe('Fiat amounts', () => {
beforeEach(() => {
cy.intercept('GET', '**/tokens/**/usdPrice', {
statusCode: 200,
body: { price: 2000 },
}).as('usdPrice')
cy.visit('/#/11155111/swap/WETH/COW')
})

it('Should change fiat amount after changing currency amount', () => {
cy.unlockCrossChainSwap()
cy.wait('@usdPrice')
getInputToken().type('1')

// Get fiat amount for 1 WETH
Expand All @@ -27,7 +32,7 @@ describe('Fiat amounts', () => {
getInputToken().clear().type('2')

// Get fiat amount for 2 WETH
getInputFiatAmount().then((fiatAmountTwoText) => {
getInputFiatAmount().should((fiatAmountTwoText) => {
const fiatAmountTwo = parseFiatAmountText(fiatAmountTwoText)
const onePercent = fiatAmountOne * 0.01

Expand Down
4 changes: 3 additions & 1 deletion apps/cowswap-frontend-e2e/src/e2e/limit-orders.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { SMALL_TIMEOUT } from '../config'

const CHAIN_ID = 11155111
const SELL_TOKEN = 'WETH'
const BUY_TOKEN = 'COW'

function unlock(): void {
cy.get('#unlock-limit-orders-btn', { timeout: 10000 }).click()
cy.get('#unlock-limit-orders-btn', { timeout: SMALL_TIMEOUT }).click()
}

function navigate(path = '', unlockLimitOrders = true): void {
Expand Down
6 changes: 5 additions & 1 deletion apps/cowswap-frontend-e2e/src/e2e/lists.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { LARGE_TIMEOUT } from '../config'

describe('Lists', () => {
beforeEach(() => {
cy.visit('/#/11155111/swap/')
})

it('change list', () => {
cy.unlockCrossChainSwap()
cy.get('#output-currency-input .open-currency-select-button', { timeout: 20_000 }).should('be.enabled').click()
cy.get('#output-currency-input .open-currency-select-button', { timeout: LARGE_TIMEOUT })
.should('be.enabled')
.click()
cy.get('#list-token-manage-button').click()
cy.get('#tokens-lists-table > div').should('have.length.greaterThan', 0)
})
Expand Down
4 changes: 3 additions & 1 deletion apps/cowswap-frontend-e2e/src/e2e/search.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { LARGE_TIMEOUT } from '../config'

function openTokenSelector(): void {
cy.get('.open-currency-select-button').first({ timeout: 20_000 }).should('be.enabled').click()
cy.get('.open-currency-select-button').first({ timeout: LARGE_TIMEOUT }).should('be.enabled').click()
}

// mock test to pass CI until we fix the test
Expand Down
13 changes: 9 additions & 4 deletions apps/cowswap-frontend-e2e/src/e2e/swap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ describe('Swap (custom)', () => {
it('can swap ETH for USDC', () => {
cy.visit(`/#/${CHAIN_ID}/swap/ETH/${USDC}`, {
onBeforeLoad: async (win) => {
const address = await win.ethereum.signer.getAddress()
mockSendCall(win.ethereum, [
handleNativeBalance(
win.ethereum,
await win.ethereum.signer.getAddress(),
address,
5n * 10n ** 18n, // 18 decimals
),
])
Expand All @@ -83,10 +84,11 @@ describe('Swap (custom)', () => {
it('Swap ETH for USDC - shows optional Switch to WETH', () => {
cy.visit(`/#/${CHAIN_ID}/swap/ETH/${USDC}`, {
onBeforeLoad: async (win) => {
const address = await win.ethereum.signer.getAddress()
mockSendCall(win.ethereum, [
handleNativeBalance(
win.ethereum,
await win.ethereum.signer.getAddress(),
address,
5n * 10n ** 18n, // 18 decimals
),
])
Expand All @@ -100,8 +102,11 @@ describe('Swap (custom)', () => {
cy.get('#output-currency-input .token-amount-input').should('not.equal', '')
acceptFeesExceedWarning()
cy.wait(1000)
cy.get('#classic-eth-flow-banner').should('contain', 'Wrap your ETH and use the classic WETH experience').click()
cy.get('#wrap-native').should('contain', 'Wrap my ETH and swap').click()
cy.get('#classic-eth-flow-banner')
.should('contain', 'Switch to the classic')
.and('contain', 'experience and benefit!')
.click()
cy.get('#switch-to-wrapped').should('contain', 'Switch to WETH').click()
})

describe('url params', () => {
Expand Down
6 changes: 4 additions & 2 deletions apps/cowswap-frontend-e2e/src/e2e/swapMod.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SMALL_TIMEOUT } from '../config'
import { handleNativeBalance, mockSendCall } from '../support/mocks/mockSendCall'

const COW = '0x0625aFB445C3B6B7B929342a04A22599fd5dBB59'
Expand Down Expand Up @@ -78,10 +79,11 @@ describe('Swap (mod)', () => {
it('can find COW and swap Native for COW', () => {
cy.visit('/#/11155111/swap', {
onBeforeLoad: async (win) => {
const address = await win.ethereum.signer.getAddress()
mockSendCall(win.ethereum, [
handleNativeBalance(
win.ethereum,
await win.ethereum.signer.getAddress(),
address,
50n * 10n ** 18n, // 18 decimals
),
])
Expand All @@ -91,7 +93,7 @@ describe('Swap (mod)', () => {
cy.swapEnterInputAmount(ETH, '0.5', true)
cy.swapSelectOutput(COW)
cy.get('#output-currency-input .token-amount-input').should('not.equal', '')
cy.get('#do-trade-button').should('contain.text', 'Swap').click({ timeout: 10000 })
cy.get('#do-trade-button').should('contain.text', 'Swap').click({ timeout: SMALL_TIMEOUT })
cy.get('#trade-confirmation > button').should('contain', 'Confirm Swap')
})
Comment thread
kernelwhisperer marked this conversation as resolved.

Expand Down
16 changes: 9 additions & 7 deletions apps/cowswap-frontend-e2e/src/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,20 @@ declare namespace Cypress {
// TODO: Add proper return type annotation
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function _clickOnToken(inputOrOutput: string) {
cy.get(`#${inputOrOutput}-currency-input .open-currency-select-button`, { timeout: 20_000 })
.should('not.be.disabled')
.click()
const buttonSelector = `#${inputOrOutput}-currency-input .open-currency-select-button`

cy.get(buttonSelector, { timeout: 20_000 }).should('not.be.disabled')
cy.get(buttonSelector).click()
}

// TODO: Add proper return type annotation
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function _selectTokenFromSelector(tokenAddress: string, inputOrOutput: string) {
cy.get(`#tokens-list div[data-address="${tokenAddress.toLowerCase()}"]`)
.scrollIntoView()
.should('be.visible')
.click({ force: true })
const tokenSelector = `#token-search-results div[data-address="${tokenAddress.toLowerCase()}"]`
const searchSelector = '#token-search-input'

cy.get(searchSelector, { timeout: 20_000 }).should('be.visible').clear().type(tokenAddress)
cy.get(tokenSelector, { timeout: 20_000 }).should('be.visible').click({ force: true })

cy.get(`#${inputOrOutput}-currency-input .token-amount-input`).should('be.visible')
}
Expand Down
7 changes: 6 additions & 1 deletion apps/cowswap-frontend-e2e/src/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ Cypress.on('window:before:load', (win) => {
}
})

Cypress.on('uncaught:exception', (err) => {
if (err.message.includes('ResizeObserver loop completed with undelivered notifications')) {
return false
}
})

const skippedUrls = [
// analytics
/ads-twitter.com/,
Expand Down Expand Up @@ -89,7 +95,6 @@ beforeEach(() => {
req.headers['origin'] = Cypress.config('baseUrl')!
req.continue()
})

skippedUrls.forEach((url) => {
cy.intercept(url, (req) => {
req.reply({ statusCode: 404 })
Expand Down
11 changes: 9 additions & 2 deletions apps/cowswap-frontend-e2e/src/support/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,22 @@ const CHAIN_NAME = 'sepolia'
const INTEGRATION_TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
assert(INTEGRATION_TEST_PRIVATE_KEY, 'INTEGRATION_TEST_PRIVATE_KEY env missing')

const INTEGRATION_TESTS_INFURA_KEY = Cypress.env('INTEGRATION_TESTS_INFURA_KEY')
const INTEGRATION_TESTS_ALCHEMY_KEY = Cypress.env('INTEGRATION_TESTS_ALCHEMY_KEY')

const NETWORK_URL = Cypress.env('REACT_APP_NETWORK_URL_' + CHAIN_ID)

const PROVIDER_URL = NETWORK_URL || `https://eth-${CHAIN_NAME}.g.alchemy.com/v2/${INTEGRATION_TESTS_ALCHEMY_KEY}`
const PROVIDER_URL =
NETWORK_URL ||
(INTEGRATION_TESTS_ALCHEMY_KEY
? `https://eth-${CHAIN_NAME}.g.alchemy.com/v2/${INTEGRATION_TESTS_ALCHEMY_KEY}`
: INTEGRATION_TESTS_INFURA_KEY
? `https://${CHAIN_NAME}.infura.io/v3/${INTEGRATION_TESTS_INFURA_KEY}`
: undefined)

assert(
PROVIDER_URL,
`PROVIDER_URL is empty, NETWORK_URL=${NETWORK_URL}, INTEGRATION_TESTS_ALCHEMY_KEY=${INTEGRATION_TESTS_ALCHEMY_KEY}`,
`PROVIDER_URL is empty, NETWORK_URL=${NETWORK_URL}, INTEGRATION_TESTS_ALCHEMY_KEY=${INTEGRATION_TESTS_ALCHEMY_KEY}, INTEGRATION_TESTS_INFURA_KEY=${INTEGRATION_TESTS_INFURA_KEY}`,
)

// address of the above key
Expand Down