mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
* add export for address txs * transfer all export from page to dialog * remove old route and components * improve file name and fix loading state of icon button * basic implementation * refetch item status * status indicator for download items * changes for multichain * handle too many requests for downloads error * update button styles and spacing * fix tests * disable async export for multichain * Fix CSV export cancel flow and stabilize context updates Applied via @cursor push command * test and review fixes * move file to client folder * round 3 * add glossary * migration tasks and skill * move skills to .agents folder * rename migrate skill * refine project rules * move MIGRATION_TASKS.md to client directory * update sym links * refine migration skills * fixes after merging the main branch --------- Co-authored-by: Hanzo Dev <[email protected]>
190 lines
6.2 KiB
Plaintext
190 lines
6.2 KiB
Plaintext
---
|
||
description: Playwright component visual tests — purpose, setup, fixtures, and conventions
|
||
globs: "*.pw.tsx"
|
||
alwaysApply: false
|
||
---
|
||
# Visual Component Tests (Playwright)
|
||
|
||
## Purpose
|
||
|
||
Playwright tests verify the visual presentation and interactive behavior of UI components across multiple viewports and color modes. Use them when a change affects what the user sees — layout, styling, conditional UI states.
|
||
|
||
## File naming and location
|
||
|
||
Test files must be named `*.pw.tsx` and placed alongside the component they test. Run all tests:
|
||
|
||
```bash
|
||
pnpm test:pw # locally (fast, no Docker)
|
||
pnpm test:pw:docker # in Docker (required for screenshot updates)
|
||
pnpm test:pw:docker path/to/Component.pw.ts --update-snapshots # Run single file in Docker and update all corresponding screenshots
|
||
```
|
||
|
||
**Never commit screenshots generated by `pnpm test:pw` (local).** Screenshots used in CI must be generated via `pnpm test:pw:docker`, otherwise tests will fail in CI due to rendering differences.
|
||
|
||
## Test projects
|
||
|
||
Three projects run against every test file:
|
||
|
||
| Project | Tag | Browser | Viewport |
|
||
|---|---|---|---|
|
||
| `default` | _(runs unless excluded with `-@default`)_ | Desktop Chrome | 1200×750 |
|
||
| `mobile` | `+@mobile` | iPhone 13 Pro (Safari) | 375×812 |
|
||
| `dark-color-mode` | `+@dark-mode` | Desktop Chrome | 1200×750, forced dark |
|
||
|
||
Tag a test to include or exclude it from a project:
|
||
|
||
```tsx
|
||
test('renders correctly +@mobile', async ({ render }) => { ... });
|
||
test('mobile only -@default +@mobile', async ({ render }) => { ... });
|
||
test('dark mode variant +@dark-mode', async ({ render }) => { ... });
|
||
```
|
||
|
||
## Imports
|
||
|
||
**Never import directly from `@playwright/experimental-ct-react`.** Use the project's extended test:
|
||
|
||
```tsx
|
||
import { test, expect } from 'playwright/index';
|
||
```
|
||
|
||
`playwright/index.ts` extends Playwright's `test` with all project fixtures and re-exports everything from `@playwright/experimental-ct-react`.
|
||
|
||
## Component mounting
|
||
|
||
Use the `render` fixture — it mounts the component inside `TestApp`, which provides the full provider stack: ChakraProvider, QueryClientProvider, SocketProvider, AppContextProvider, SettingsContextProvider, GrowthBookProvider, WagmiProvider, RewardsContextProvider, and CsvExportContextProvider.
|
||
|
||
```tsx
|
||
test('example', async ({ render }) => {
|
||
const component = await render(<MyComponent prop="value" />);
|
||
await expect(component).toHaveScreenshot();
|
||
});
|
||
```
|
||
|
||
`render` accepts optional second and third arguments to customize `hooksConfig` (mainly used for mocking Next.js router object), `appContext` and `marketplaceContext` props passed to `TestApp`.
|
||
|
||
## Fixtures
|
||
|
||
All fixtures are injected via the `test` function. Available fixtures:
|
||
|
||
### `mockApiResponse`
|
||
Intercepts Blockscout API calls. Returns the mocked URL.
|
||
|
||
```tsx
|
||
await mockApiResponse('token', tokenMock, {
|
||
pathParams: { hash: '0x...' },
|
||
queryParams: { tab: 'holders' },
|
||
status: 200, // optional, default 200
|
||
times: 1, // optional, intercept N times
|
||
});
|
||
```
|
||
|
||
### `mockEnvs`
|
||
Sets environment variables for the test (stored in localStorage, read by `getEnvValue`). Use the built-in `ENVS_MAP` for common configs:
|
||
|
||
```tsx
|
||
await mockEnvs([
|
||
['NEXT_PUBLIC_ROLLUP_TYPE', 'optimistic'],
|
||
['NEXT_PUBLIC_API_HOST', 'localhost'],
|
||
]);
|
||
```
|
||
|
||
### `mockFeatures`
|
||
Sets feature flags in localStorage:
|
||
|
||
```tsx
|
||
await mockFeatures([
|
||
['account', true],
|
||
['rewards', false],
|
||
]);
|
||
```
|
||
|
||
### `mockAssetResponse`
|
||
Routes any URL to serve a file from disk:
|
||
|
||
```tsx
|
||
await mockAssetResponse('https://example.com/logo.png', './playwright/mocks/duck.png');
|
||
```
|
||
|
||
### `mockConfigResponse`
|
||
Routes an external config asset URL (JSON or image) derived from an env var name:
|
||
|
||
```tsx
|
||
await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://example.com/config.json', content);
|
||
```
|
||
|
||
### `mockRpcResponse`
|
||
Mocks RPC calls (typed against viem's `PublicRpcSchema`):
|
||
|
||
```tsx
|
||
await mockRpcResponse([{ method: 'eth_blockNumber', result: '0x1' }]);
|
||
```
|
||
|
||
### `mockContractReadResponse`
|
||
Mocks `eth_call` for a specific contract read:
|
||
|
||
```tsx
|
||
await mockContractReadResponse({
|
||
abiItem: erc20ABI[0],
|
||
args: ['0x123'],
|
||
address: '0x00ab',
|
||
result: '1000000',
|
||
});
|
||
```
|
||
|
||
### `createSocket`
|
||
Creates a local WebSocket server on port 3200. Resolves when a client connects:
|
||
|
||
```tsx
|
||
const socket = await createSocket();
|
||
// push events via socket
|
||
```
|
||
|
||
### `mockTextAd`
|
||
Mocks the text ad provider (Sevio + duck ad data). Call this in tests that render ad-containing layouts to avoid network requests and visual noise.
|
||
|
||
### `injectMetaMaskProvider`
|
||
Injects `window.ethereum` with `isMetaMask: true`:
|
||
|
||
```tsx
|
||
await injectMetaMaskProvider();
|
||
```
|
||
|
||
### `auth` helpers
|
||
For tests requiring an authenticated user, use the `contextWithAuth` fixture or call `authenticateUser(context)` to set the API token cookie.
|
||
|
||
### `mockMultichainConfig`
|
||
Injects `window.__multichainConfig` with 5 mock chains (chainA–E). Use in tests that render multichain UI.
|
||
|
||
## Selectors
|
||
|
||
Always use semantic selectors — never CSS class names. CSS classes are implementation details and break when styling changes.
|
||
|
||
```tsx
|
||
// BAD
|
||
page.locator('.tx-hash-link')
|
||
|
||
// GOOD
|
||
page.getByRole('link', { name: /transaction/i })
|
||
page.getByLabel('Address menu')
|
||
page.getByText('0x1234…')
|
||
page.getByTestId('tx-hash')
|
||
```
|
||
|
||
## Environment and date
|
||
|
||
`playwright/lib.tsx` runs before every component mount and:
|
||
- Mocks `next/router.useRouter()` with a configurable router object.
|
||
- Fixes the current date to `2022-11-11T12:00:00Z` via MockDate — all `new Date()` / `Date.now()` calls return this value.
|
||
|
||
## Mock data
|
||
|
||
Use existing files from `mocks/` and `playwright/mocks/` rather than hardcoding values inline. The `mocks/` directory contains shared mock data (API responses, entities) used across both Vitest and Playwright tests. `playwright/mocks/` contains Playwright-specific assets (images, JSON files, module mocks).
|
||
|
||
Extract any non-obvious test values (IDs, amounts, hashes) into named constants above the test suite.
|
||
|
||
## Conventions
|
||
|
||
- Do not use the `testFn: TestFixture<...>` pattern. It makes refactoring harder and can bypass Playwright lint rules. Use it only when genuinely sharing fixture logic across multiple suites.
|
||
- Keep each test focused on one visual state or interaction.
|
||
- Screenshots are the contract — keep them up to date and review diffs carefully in PRs.
|