Skip to content
Open
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: 8 additions & 0 deletions apps/cowswap-e2e-tests/src/pages/SwapPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ export class SwapPage implements TradePage {
* header has its own, differently-cased "Connect wallet" button, so this is matched `exact`.
*/
readonly connectWalletButton: Locator
/**
* The collapsed "Trading mode" dropdown that hides the Swap/Limit/TWAP/Hooks nav links at
* viewports where `TradeWidgetForm`'s `showDropdown` is true. Matched by a generated
* styled-components class (`displayName` babel option) — fragile, but there's no stable
* id/`data-testid` on `DropdownButton` yet.
*/
readonly tradingModeDropdown: Locator

constructor(page: Page) {
this.page = page
Expand Down Expand Up @@ -110,6 +117,7 @@ export class SwapPage implements TradePage {
this.wrapButton = page.getByRole('button', { name: 'Wrap', exact: true })
this.unwrapButton = page.getByRole('button', { name: 'Unwrap', exact: true })
this.connectWalletButton = page.getByRole('button', { name: 'Connect Wallet', exact: true })
this.tradingModeDropdown = page.locator('[class*="styled__DropdownButton"]')
}

async goto(opts: { chainId: number; sell?: string; buy?: string }): Promise<void> {
Expand Down
28 changes: 28 additions & 0 deletions apps/cowswap-e2e-tests/src/support/mockBridgeSupportedTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { BrowserContext } from '@playwright/test'

export interface MockedBridgeToken {
address: string
symbol: string
name: string
decimals: number
chainId: number
}

/**
* Stubs the Bungee/Socket bridge provider's `/dest-tokens` endpoint (`BungeeApi.getBuyTokens` in
* `@cowprotocol/sdk-bridging`), which both `useBridgeSupportedTokens` (resolving/listing buyable
* tokens on a target chain) and `useRoutesAvailability` (is this destination chain reachable at
* all) call under the hood — mocking this one REST call satisfies both, since Bungee is the only
* enabled provider (`bridgingSdk.setAvailableProviders([bungeeBridgeProvider.info.dappId])`).
* Response shape: `{ success: true, result: TokenInfo[] }`, where the SDK maps each entry's
* `logoURI` to `logoUrl` and otherwise passes it through as-is.
*/
export async function mockBridgeSupportedTokens(context: BrowserContext, tokens: MockedBridgeToken[]): Promise<void> {
await context.route(/bungee-manual\/dest-tokens/i, async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, result: tokens }),
})
})
}
24 changes: 24 additions & 0 deletions apps/cowswap-e2e-tests/src/support/mockHookLogo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import path from 'path'

import type { BrowserContext, Route } from '@playwright/test'

const BUILD_CUSTOM_HOOK_LOGO_PATH = path.resolve(
__dirname,
'../../../cowswap-frontend/src/modules/hooksStore/dapps/BuildHookApp/build.png',
)

/**
* Stubs `hookDappsRegistry.ts`'s `BUILD_CUSTOM_HOOK.image` — a `raw.githubusercontent.com` URL with no
* test-side mock and no frontend-side `onError` fallback (`HookItem` just renders `<img src={...} />`
* with nothing to show if the request fails). Any hiccup reaching GitHub from the test runner renders a
* bare placeholder box instead of the hook's hard-hat icon. Fulfilling from the same `build.png` already
* checked into the frontend source removes that live network dependency entirely.
*/
export async function mockHookLogo(context: BrowserContext): Promise<void> {
await context.route(
/raw\.githubusercontent\.com\/cowprotocol\/cowswap\/.*\/BuildHookApp\/build\.png/i,
async (route: Route) => {
await route.fulfill({ status: 200, contentType: 'image/png', path: BUILD_CUSTOM_HOOK_LOGO_PATH })
},
)
}
25 changes: 25 additions & 0 deletions apps/cowswap-e2e-tests/src/support/mockHooksSimulation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { BrowserContext, Route } from '@playwright/test'

/**
* Stubs the Tenderly bundle-simulation endpoint (`bundleSimulation.ts`'s `simulateBundle`, hit at
* `{BFF_BASE_URL}/{chainId}/simulation/simulateBundle`) that `useTenderlyBundleSimulation` POSTs to
* once the trade-confirmation screen mounts with at least one hook attached. Real endpoint, never
* mocked before this — `HookItem` renders "Simulation successful"/"Simulation failed" straight off
* each response entry's `status` boolean, positionally matched to the posted pre/post hooks.
*/
export async function mockHooksSimulation(context: BrowserContext, opts: { status?: boolean } = {}): Promise<void> {
const status = opts.status ?? true

await context.route(/bff\.(?:barn\.)?cow\.fi\/\d+\/simulation\/simulateBundle/i, async (route: Route) => {
const postedHooks = JSON.parse(route.request().postData() || '[]') as unknown[]
const body = postedHooks.map((_, index) => ({
link: `https://dashboard.tenderly.co/mock-simulation-${index}`,
status,
id: `mock-simulation-${index}`,
cumulativeBalancesDiff: {},
stateDiff: [],
gasUsed: '21000',
}))
await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(body) })
})
}
41 changes: 41 additions & 0 deletions apps/cowswap-e2e-tests/src/support/mockTokenLogos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getAddressKey } from '@cowprotocol/cow-sdk'

import type { BrowserContext, Route } from '@playwright/test'

// This suite's Sepolia test-token deployments (`WETH`/`USDC` in `market-orders.spec.ts`) aren't on
// any token list, so every URL `useTokenLogoUrl` tries — keyed off these same addresses on every
// chain it checks — 403s against the real CDN (verified directly). Redirecting to each token's real
// mainnet counterpart resolves to the actual brand icon instead of a broken image or a letter avatar.
const REAL_MAINNET_ADDRESS: Record<string, string> = {
[getAddressKey('0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14')]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
[getAddressKey('0xbe72E441BF55620febc26715db68d3494213D8Cb')]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const LOGO_PATH_RE = /\/token-lists\/images\/\d+\/(0x[a-fA-F0-9]{40})\/logo\.png$/i

/**
* Stubs `cowprotocolTokenLogoUrl`'s CDN endpoint (`files.cow.fi/token-lists/images/:chainId/:address/logo.png`)
* for this suite's test-token addresses, redirecting each to its real mainnet counterpart's logo
* (same technique as `route.fetch()`'s upstream-merge helpers elsewhere in this folder, just fetching
* a different URL instead of the same one). Anything not in `REAL_MAINNET_ADDRESS` falls back to a
* plain 404, matching the real CDN's own behavior for an unlisted token — that keeps `TokenLogo`'s
* `onError` fallback (`SingleLetterLogo`) intact rather than masking it with a fake "successful" image.
*/
export async function mockTokenLogos(context: BrowserContext): Promise<void> {
await context.route(/files\.cow\.fi\/token-lists\/images\//i, async (route: Route) => {
const match = new URL(route.request().url()).pathname.match(LOGO_PATH_RE)
const realAddress = match && REAL_MAINNET_ADDRESS[getAddressKey(match[1])]

if (!realAddress) {
await route.fulfill({ status: 404 })
return
}

try {
const response = await route.fetch({ url: `https://files.cow.fi/token-lists/images/1/${realAddress}/logo.png` })
await route.fulfill({ response })
} catch {
await route.fulfill({ status: 404 })
}
})
}
Loading