Skip to content

Commit 2b4b6ec

Browse files
authored
Merge pull request #15550 from ethereum/e2e-tests
e2e tests playwright & chromatic
2 parents c9ae039 + 4ce2caf commit 2b4b6ec

27 files changed

+1744
-44
lines changed

.github/workflows/playwright.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: E2E tests
2+
on:
3+
push:
4+
branches: [master, staging, e2e-tests] # TODO: remove e2e-tests branch after testing
5+
pull_request:
6+
branches: [master, staging, e2e-tests] # TODO: remove e2e-tests branch after testing
7+
jobs:
8+
playwright:
9+
runs-on: ubuntu-latest
10+
env:
11+
CI: true
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Wait for Netlify Deploy
16+
id: netlify_deploy
17+
uses: pettinarip/[email protected]
18+
with:
19+
site_id: "e8f2e766-888b-4954-8500-1b647d84db99"
20+
max_timeout: 3600
21+
env:
22+
NETLIFY_TOKEN: ${{ secrets.NETLIFY_TOKEN }}
23+
24+
- uses: actions/setup-node@v4
25+
with:
26+
node-version: lts/*
27+
28+
- name: Setup pnpm
29+
uses: pnpm/action-setup@v2
30+
31+
- name: Install dependencies
32+
run: pnpm install
33+
34+
- name: Install Playwright with all browsers
35+
run: npx playwright install --with-deps
36+
37+
- name: Run E2E Tests on Netlify URL
38+
run: pnpm test:e2e
39+
env:
40+
PLAYWRIGHT_TEST_BASE_URL: ${{ steps.netlify_deploy.outputs.url }}
41+
42+
- uses: actions/upload-artifact@v4
43+
if: always()
44+
with:
45+
name: playwright-report
46+
path: ./tests/e2e/__results__
47+
retention-days: 7
48+
49+
chromatic:
50+
name: chromatic
51+
needs: playwright
52+
runs-on: ubuntu-latest
53+
steps:
54+
- uses: actions/checkout@v4
55+
with:
56+
fetch-depth: 0
57+
- uses: actions/setup-node@v4
58+
with:
59+
node-version: 22.12.0
60+
- name: Setup pnpm
61+
uses: pnpm/action-setup@v2
62+
63+
- name: Install dependencies
64+
run: pnpm install
65+
66+
- name: Download Playwright test results
67+
uses: actions/download-artifact@v4
68+
with:
69+
name: playwright-report
70+
path: ./tests/e2e/__results__
71+
72+
- name: Run Chromatic
73+
uses: chromaui/action@latest
74+
with:
75+
projectToken: ${{ secrets.CHROMATIC_E2E_TOKEN }}
76+
playwright: true
77+
exitZeroOnChanges: true
78+
storybookBaseDir: .
79+
env:
80+
CHROMATIC_ARCHIVE_LOCATION: ./tests/e2e/__results__

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,5 @@ src/data/crowdin/bucketsAwaitingReviewReport.csv
6363

6464
# Storybook
6565
build-storybook.log
66+
build-archive.log
6667
storybook-static

chromatic.config.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

docs/e2e-testing.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# E2E Testing with Playwright
2+
3+
## Overview
4+
5+
This project uses [Playwright](https://playwright.dev/) for end-to-end testing with [Chromatic](https://www.chromatic.com/) integration for visual regression testing.
6+
7+
## Quick Start
8+
9+
### Running Tests Locally
10+
11+
```bash
12+
# Install dependencies (if not already done)
13+
pnpm install
14+
15+
# Install Playwright browsers
16+
npx playwright install
17+
18+
# Start development server (in a separate terminal)
19+
pnpm dev
20+
21+
# Run all e2e tests
22+
pnpm test:e2e
23+
24+
# Run tests with UI (interactive mode)
25+
pnpm test:e2e:ui
26+
27+
# Run tests in debug mode
28+
pnpm test:e2e:debug
29+
30+
# View test report
31+
pnpm test:e2e:report
32+
```
33+
34+
### Development Server: Choosing the Best Mode for E2E Testing
35+
36+
By default, Playwright tests run against `http://localhost:3000`. Before running tests, ensure your development server is up and running. You can start it in development mode with:
37+
38+
```bash
39+
pnpm dev
40+
```
41+
42+
**Note:** Running in dev mode (`pnpm dev`) is convenient for rapid iteration, but it can be significantly slower than production builds. As a result, you may encounter Playwright timeout errors if pages take too long to load or build on demand.
43+
44+
#### Tips to Avoid Timeout Issues
45+
46+
- **Preload pages:** Manually visit the routes you plan to test in your browser before running the tests. This triggers Next.js to build those pages ahead of time, reducing load times during testing.
47+
- **Increase Playwright timeouts:** If you must use dev mode, consider increasing Playwright's default timeouts to accommodate slower builds (see Playwright config docs).
48+
- **Use a production build for reliability:** For the fastest and most stable E2E test runs, use a production build. This ensures all pages are prebuilt and served at optimal speed:
49+
50+
```bash
51+
pnpm build
52+
pnpm start
53+
```
54+
55+
This will serve your app at `http://localhost:3000` in production mode, minimizing the risk of timeouts and making your tests more reliable.
56+
57+
> **Summary:**
58+
>
59+
> - Use `pnpm dev` for quick local development and debugging, but expect slower performance and possible timeouts.
60+
> - For CI or full test runs, prefer `pnpm build && pnpm start` for best results.
61+
62+
## Directory Layout
63+
64+
```
65+
tests/e2e/
66+
├── __results__/ # Test results (gitignored)
67+
├── __report__/ # HTML reports (gitignored)
68+
├── fixtures/ # Test data and fixtures
69+
├── pages/ # Page Object Model classes
70+
├── utils/ # Test utilities and helpers
71+
├── *.spec.ts # Test files
72+
└── .gitignore
73+
```
74+
75+
## Writing Tests
76+
77+
### Best Practices
78+
79+
#### 1. Use Page Object Model
80+
81+
Create reusable page objects for common interactions. For more details, see the [Playwright Page Object Model documentation](https://playwright.dev/docs/pom):
82+
83+
```typescript
84+
// pages/HomePage.ts
85+
export class HomePage {
86+
constructor(private page: Page) {}
87+
88+
async goto() {
89+
await this.page.goto("/")
90+
}
91+
92+
async searchFor(query: string) {
93+
const isMobile = await this.isMobileViewport()
94+
if (isMobile) {
95+
await this.page.getByTestId("search-button").first().click()
96+
} else {
97+
await this.page.getByTestId("search-input-button").first().click()
98+
}
99+
await this.page.getByPlaceholder("Search").fill(query)
100+
}
101+
102+
private async isMobileViewport() {
103+
const viewport = this.page.viewportSize()
104+
return viewport && viewport.width <= 768
105+
}
106+
}
107+
```
108+
109+
#### 2. Robust Selectors
110+
111+
Prefer `data-testid` attributes over CSS selectors:
112+
113+
```typescript
114+
// Good
115+
await page.getByTestId("search-button")
116+
117+
// Better for accessibility
118+
await page.getByRole("button", { name: "Search" })
119+
120+
// Avoid fragile selectors
121+
await page.locator(".search-btn-class") // Fragile
122+
```
123+
124+
#### 3. Responsive Testing
125+
126+
Handle different viewport sizes appropriately:
127+
128+
```typescript
129+
test("search functionality", async ({ page }) => {
130+
const viewport = page.viewportSize()
131+
const isMobile = viewport && viewport.width <= breakpointAsNumber.md
132+
133+
if (isMobile) {
134+
// Mobile-specific logic
135+
} else {
136+
// Desktop-specific logic
137+
}
138+
})
139+
```
140+
141+
#### 4. Visual Testing
142+
143+
Use Chromatic snapshots for visual regression testing:
144+
145+
```typescript
146+
import { takeSnapshot } from "@chromatic-com/playwright"
147+
148+
test("visual regression", async ({ page }, testInfo) => {
149+
await page.goto("/")
150+
await takeSnapshot(page, "homepage-initial", testInfo)
151+
})
152+
```

package.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
"events-import": "ts-node -O '{ \"module\": \"commonjs\" }' src/scripts/events-import.ts",
2424
"crowdin-needs-review": "ts-node -O '{ \"module\": \"commonjs\" }' src/scripts/crowdin/reports/generateReviewReport.ts",
2525
"update-tutorials": "ts-node -O '{ \"module\": \"commonjs\" }' src/scripts/update-tutorials-list.ts",
26-
"prepare": "husky"
26+
"prepare": "husky",
27+
"test:e2e": "playwright test",
28+
"test:e2e:ui": "playwright test --ui",
29+
"test:e2e:debug": "playwright test --debug",
30+
"test:e2e:report": "playwright show-report tests/e2e/__report__"
2731
},
2832
"dependencies": {
2933
"@crowdin/crowdin-api-client": "^1.25.0",
@@ -97,8 +101,10 @@
97101
"yaml-loader": "^0.8.0"
98102
},
99103
"devDependencies": {
104+
"@chromatic-com/playwright": "^0.12.4",
100105
"@chromatic-com/storybook": "1.5.0",
101106
"@netlify/plugin-nextjs": "^5.10.0",
107+
"@playwright/test": "^1.52.0",
102108
"@storybook/addon-essentials": "8.6.14",
103109
"@storybook/addon-interactions": "8.6.14",
104110
"@storybook/addon-links": "8.6.14",
@@ -120,7 +126,7 @@
120126
"@typescript-eslint/eslint-plugin": "^7.18.0",
121127
"@typescript-eslint/parser": "^7.18.0",
122128
"autoprefixer": "^10.4.19",
123-
"chromatic": "10.9.6",
129+
"chromatic": "12.0.0",
124130
"decompress": "^4.2.1",
125131
"dotenv": "^16.5.0",
126132
"eslint": "^8.57.1",
@@ -152,5 +158,5 @@
152158
"unist-util-visit": "^5.0.0",
153159
"xml2js": "^0.6.2"
154160
},
155-
"packageManager": "[email protected].3+sha512.467df2c586056165580ad6dfb54ceaad94c5a30f80893ebdec5a44c5aa73c205ae4a5bb9d5ed6bb84ea7c249ece786642bbb49d06a307df218d03da41c317417"
161+
"packageManager": "[email protected].4"
156162
}

playwright.config.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import path from "path"
2+
3+
import dotenv from "dotenv"
4+
import type { ChromaticConfig } from "@chromatic-com/playwright"
5+
import { defineConfig, devices } from "@playwright/test"
6+
7+
dotenv.config({ path: path.resolve(__dirname, ".env.local") })
8+
9+
export default defineConfig<ChromaticConfig>({
10+
testDir: "./tests/e2e",
11+
outputDir: "./tests/e2e/__results__",
12+
fullyParallel: true,
13+
forbidOnly: !!process.env.CI,
14+
retries: process.env.CI ? 2 : 0,
15+
workers: process.env.CI ? 1 : 3,
16+
reporter: [
17+
["html", { outputFolder: "./tests/e2e/__report__" }],
18+
["line"],
19+
process.env.CI ? ["github"] : ["list"],
20+
],
21+
use: {
22+
baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || "http://localhost:3000",
23+
trace: "on-first-retry",
24+
screenshot: "only-on-failure",
25+
26+
// Global test timeout
27+
actionTimeout: 10000,
28+
navigationTimeout: 30000,
29+
30+
// Chromatic settings
31+
disableAutoSnapshot: true,
32+
},
33+
34+
// Global test timeout
35+
timeout: 30000,
36+
37+
// Expect timeout
38+
expect: {
39+
timeout: 10000,
40+
},
41+
projects: [
42+
/* Test against desktop browsers */
43+
{
44+
name: "chromium",
45+
use: { ...devices["Desktop Chrome"] },
46+
},
47+
{
48+
name: "webkit",
49+
use: { ...devices["Desktop Safari"] },
50+
},
51+
/* Test against mobile viewports. */
52+
{
53+
name: "Mobile Chrome",
54+
use: { ...devices["Pixel 5"] },
55+
},
56+
{
57+
name: "Mobile Safari",
58+
use: { ...devices["iPhone 12"] },
59+
},
60+
],
61+
})

0 commit comments

Comments
 (0)