Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
147 commits
Select commit Hold shift + click to select a range
1ccf351
docs(plan): add Playwright + Chromatic page visual tests plan
pettinarip Apr 15, 2026
cf718e8
feat(tests): add playwright + chromatic page visual regression tests
pettinarip Apr 21, 2026
2942aeb
test(visual): wait for nav hydration and cover each src/layouts layout
pettinarip Apr 21, 2026
f7fbbe2
chore(chromatic): zip archive uploads in chromatic:pages
pettinarip Apr 21, 2026
d04c41d
chore(chromatic): align visual test ci with official playwright pattern
pettinarip Apr 21, 2026
9751ae7
chore(chromatic): group chromatic:pages with chromatic script
pettinarip Apr 21, 2026
21e6528
chore(chromatic): harden visual test workflow installs
pettinarip Apr 21, 2026
4af8be7
test(visual): wait on domcontentloaded and default stable selector
pettinarip Apr 21, 2026
3b81d9d
ci(chromatic): align page visual test pr triggers with chromatic.yml
pettinarip Apr 21, 2026
c58190f
chore(visual): trim noisy comments in visual test config and spec
pettinarip Apr 21, 2026
1b7db86
fix(chromatic): delegate test:visual:build to pnpm build for webpack …
pettinarip Apr 21, 2026
586226c
Merge remote-tracking branch 'origin/dev' into feat/playwright-chroma…
pettinarip Apr 21, 2026
f03561b
chore(deps): regenerate pnpm-lock.yaml after dev merge
pettinarip Apr 21, 2026
4cfc51b
fix(playwright): only start webServer for chromatic visual tests
pettinarip Apr 21, 2026
da217ee
chore(playwright): extract visual tests into dedicated config
pettinarip Apr 21, 2026
7ce97f4
ci(chromatic): allow manual dispatch for page visual workflow
pettinarip Apr 21, 2026
e3a6105
ci(chromatic): temporarily include dev branch for test run
pettinarip Apr 21, 2026
b02ec67
chore(docs): untrack local planning doc
pettinarip Apr 22, 2026
2fe4d2b
test(visual): wait for all data-slot=loading indicators before snapshot
pettinarip Apr 22, 2026
590b7e8
chore(skills): add page-visual-tests skill
pettinarip Apr 22, 2026
3d2fb3b
refactor(visual): drop /en prefix, stableselector, and devices spread
pettinarip Apr 22, 2026
ee5bf6c
Merge remote-tracking branch 'origin/dev' into feat/playwright-chroma…
pettinarip Apr 22, 2026
d4eab3e
fix(visual): stabilize dynamic content across snapshots
pettinarip Apr 22, 2026
3b2201a
feat: bidirectional webPageId and articleId
myelinated-wackerow Apr 24, 2026
1382533
fix(visual): wait for ssr:false hydration and image load before snapshot
pettinarip Apr 27, 2026
796d70a
Merge remote-tracking branch 'origin/dev' into feat/playwright-chroma…
pettinarip Apr 27, 2026
1f2458f
chore(deps): regenerate pnpm-lock.yaml after dev merge
pettinarip Apr 27, 2026
306594f
fix(visual): share maybeShuffle across apps and staking grids
pettinarip Apr 27, 2026
4e88a09
fix(visual): also wait for next/image blur placeholder removal
pettinarip Apr 27, 2026
2259ea9
ci(visual): bump chromaui/action to v16 to match chromatic 16.x
pettinarip Apr 27, 2026
5a42aa4
Revert "ci(chromatic): temporarily include dev branch for test run"
pettinarip Apr 27, 2026
a4d20fa
Merge remote-tracking branch 'origin/dev' into feat/playwright-chroma…
pettinarip Apr 27, 2026
15b0dd8
Merge remote-tracking branch 'origin/dev' into feat/playwright-chroma…
pettinarip Apr 28, 2026
4745380
fix(staking): remove fixed min-height on product card tags row
konopkja Apr 30, 2026
7d1ea76
patch: margin, dir-agnostic style, deprecation warning
myelinated-wackerow Apr 30, 2026
4cf684d
content: update what-is-ethereum roadmap section from 2025 to 2026
Apr 30, 2026
c753991
content(get-eth): update Bancor DEX name to Carbon DeFi
Apr 30, 2026
9c74338
content(homepage): add USD currency label to trading volume stat
Apr 30, 2026
fb31309
fix(ui): ExpandableCard layout and coloring
myelinated-wackerow Apr 30, 2026
499e597
Merge branch 'dev' into fix/saas-card-tags-whitespace
myelinated-wackerow Apr 30, 2026
f20a41e
Merge branch 'dev' into fix/saas-card-tags-whitespace
myelinated-wackerow Apr 30, 2026
db34372
Merge branch 'dev' into fix/roadmap-title-year-2026
myelinated-wackerow Apr 30, 2026
c1c5d83
Merge branch 'dev' into fix/bancor-rebrand-carbon-defi
myelinated-wackerow Apr 30, 2026
e9fabc8
Merge branch 'dev' into fix/homepage-trading-volume-currency
myelinated-wackerow Apr 30, 2026
4a0dc70
content(layer-2): refresh page copy to reflect Fusaka/PeerDAS (Dec 2025)
May 1, 2026
e1ce6f1
fix(seo): align /get-eth meta title and description with page content
May 1, 2026
47ef6c2
fix(seo): improve /wallets/find-wallet meta title and description
May 1, 2026
aabf07f
fix(seo): use CollectionPage for /layer-2/networks JSON-LD
nloureiro May 1, 2026
3bfda9c
content(apps): remove orphan Scout Game description
nloureiro May 1, 2026
00438b6
content(grants): add Etherscan Grant Directory to comprehensive direc…
nloureiro May 1, 2026
6958e89
Merge pull request #18109 from ethereum/staging
pettinarip May 1, 2026
07dd0e6
Merge remote-tracking branch 'origin/dev' into HEAD
pettinarip May 1, 2026
f7ea7a2
patch: alignment, spacing, rm redundant nested div
myelinated-wackerow May 1, 2026
6e513cb
patch: spacing
myelinated-wackerow May 1, 2026
c821a92
Merge pull request #18089 from nloureiro/fix/bancor-rebrand-carbon-defi
wackerow May 1, 2026
40761b7
patch: use currency formatting for trading volume
myelinated-wackerow May 1, 2026
20d94f4
Merge pull request #18100 from nloureiro/fix/get-eth-meta-title-align…
wackerow May 1, 2026
fb259d9
chore: apply sentence casing to title
wackerow May 1, 2026
42f8930
Update chains data
actions-user May 1, 2026
eccd8ec
Merge pull request #18101 from nloureiro/fix/find-wallet-seo-meta
wackerow May 1, 2026
2a2a441
Merge pull request #18102 from ethereum/fix/17724-collection-page-lay…
wackerow May 1, 2026
3800bdf
Merge pull request #18103 from ethereum/fix/18049-remove-scout-game-o…
wackerow May 1, 2026
cf97bbc
chore: align text styling
wackerow May 1, 2026
130c28b
Merge pull request #18105 from ethereum/content/17712-add-etherscan-g…
wackerow May 1, 2026
c3c5133
Merge pull request #18088 from nloureiro/fix/roadmap-title-year-2026
wackerow May 1, 2026
a3de998
Merge pull request #18090 from nloureiro/fix/homepage-trading-volume-…
wackerow May 1, 2026
ba3dd24
patch: apply updates from content review
wackerow May 1, 2026
102e7d6
Merge pull request #18096 from nloureiro/content/layer-2-post-fusaka-…
wackerow May 1, 2026
53d7e2b
i18n(ar): LLM translation
wackerow May 1, 2026
a28c1fa
i18n(bn): LLM translation
wackerow May 1, 2026
26089d6
i18n(cs): LLM translation
wackerow May 1, 2026
f456981
i18n(de): LLM translation
wackerow May 1, 2026
af57f57
i18n(es): LLM translation
wackerow May 1, 2026
3d316c0
i18n(fr): LLM translation
wackerow May 1, 2026
21169f5
i18n(hi): LLM translation
wackerow May 1, 2026
7b86d54
i18n(id): LLM translation
wackerow May 1, 2026
5e1be83
i18n(it): LLM translation
wackerow May 1, 2026
fd158b4
i18n(ja): LLM translation
wackerow May 1, 2026
66f490d
i18n(ko): LLM translation
wackerow May 1, 2026
621502d
i18n(mr): LLM translation
wackerow May 1, 2026
047a27f
i18n(pl): LLM translation
wackerow May 1, 2026
b913385
i18n(pt-br): LLM translation
wackerow May 1, 2026
f3c49d2
i18n(ru): LLM translation
wackerow May 1, 2026
8ff9341
i18n(sw): LLM translation
wackerow May 1, 2026
fd57598
i18n(ta): LLM translation
wackerow May 1, 2026
5234ac6
i18n(te): LLM translation
wackerow May 1, 2026
24efd42
i18n(tr): LLM translation
wackerow May 1, 2026
a55adb2
i18n(uk): LLM translation
wackerow May 1, 2026
86a7c7c
i18n(ur): LLM translation
wackerow May 1, 2026
319f8ed
i18n(vi): LLM translation
wackerow May 1, 2026
7e14afe
i18n(zh): LLM translation
wackerow May 1, 2026
1f5c5b0
i18n(zh-tw): LLM translation
wackerow May 1, 2026
6e8cef1
i18n: merge tmp-intl/run-0501-1704 into intl/pending-dev
wackerow May 1, 2026
4a12cb9
Merge pull request #18111 from ethereum/intl/pending-dev
wackerow May 1, 2026
36c0140
patch: layer-2/ og description verbosity
myelinated-wackerow May 1, 2026
7040d4d
fix: heading levels on find-wallet page
myelinated-wackerow May 1, 2026
9ea1d06
i18n(ar): LLM translation
wackerow May 1, 2026
2202b29
i18n(bn): LLM translation
wackerow May 1, 2026
b7c3317
i18n(cs): LLM translation
wackerow May 1, 2026
0018e39
i18n(de): LLM translation
wackerow May 1, 2026
8cb81e9
i18n(es): LLM translation
wackerow May 1, 2026
aeeaf0b
i18n(fr): LLM translation
wackerow May 1, 2026
5d216b7
i18n(hi): LLM translation
wackerow May 1, 2026
08355ff
i18n(id): LLM translation
wackerow May 1, 2026
17f801b
i18n(it): LLM translation
wackerow May 1, 2026
65034c2
i18n(ja): LLM translation
wackerow May 1, 2026
36f3c94
i18n(ko): LLM translation
wackerow May 1, 2026
53fb316
i18n(mr): LLM translation
wackerow May 1, 2026
c95958b
i18n(pl): LLM translation
wackerow May 1, 2026
36a76df
i18n(pt-br): LLM translation
wackerow May 1, 2026
bb0844e
i18n(ru): LLM translation
wackerow May 1, 2026
fb03040
i18n(sw): LLM translation
wackerow May 1, 2026
29115d5
i18n(ta): LLM translation
wackerow May 1, 2026
a53bd15
i18n(te): LLM translation
wackerow May 1, 2026
a0cfd68
i18n(tr): LLM translation
wackerow May 1, 2026
542b876
i18n(uk): LLM translation
wackerow May 1, 2026
084e10e
i18n(ur): LLM translation
wackerow May 1, 2026
27abebc
i18n(vi): LLM translation
wackerow May 1, 2026
8214e2c
i18n(zh): LLM translation
wackerow May 1, 2026
d0096f4
i18n(zh-tw): LLM translation
wackerow May 1, 2026
1a062de
i18n: sanitize translation output
wackerow May 1, 2026
72b30a7
i18n: merge tmp-intl/run-0501-1937 into intl/pending-dev
wackerow May 1, 2026
070e5bf
feat(data-layer): expose eth 24hr percent change
pettinarip May 2, 2026
b4a1f65
fix(eth-price-card): restore 24hr change indicator with stable layout
pettinarip May 2, 2026
41cbb9b
review feedback
pettinarip May 2, 2026
d64d2f3
regenerate lock file
pettinarip May 2, 2026
a021ffc
patch(ui): listing methodology font style
myelinated-wackerow May 2, 2026
ce42e7f
i18n: review fixes across 8 languages
myelinated-wackerow May 4, 2026
22b8c92
i18n(ta): fix non-fungible mistranslation
myelinated-wackerow May 4, 2026
f1ed565
Merge pull request #18009 from ethereum/feat/playwright-chromatic-pag…
pettinarip May 5, 2026
af4733f
Updated link to go-eth-kzg
fredrik0x May 5, 2026
3e193ed
Merge pull request #18113 from ethereum/intl/pending-dev
wackerow May 5, 2026
12d3cef
Merge pull request #18127 from ethereum/fredrik0x-patch-1
wackerow May 6, 2026
badc44a
chore: update chain names for latest chain data
wackerow May 6, 2026
020684b
Merge pull request #18081 from konopkja/fix/saas-card-tags-whitespace
wackerow May 6, 2026
8ade2bb
Merge pull request #18110 from ethereum/automated-update-20260501163822
wackerow May 6, 2026
263494b
fix: wallet logo span, title alignment
wackerow May 6, 2026
37d7072
Merge pull request #18138 from ethereum/patch-wallet-logo-title
pettinarip May 7, 2026
d5eb1e6
fix(seo): gate translation status on content presence, not namespace …
pettinarip May 7, 2026
1776b96
Merge pull request #18115 from ethereum/methodology-font-size
pettinarip May 8, 2026
a0d8833
Merge pull request #18112 from ethereum/seo-patches-2026-05-01
pettinarip May 8, 2026
518fd8c
Merge pull request #18091 from ethereum/expandable-card
pettinarip May 8, 2026
05ccb7b
Merge remote-tracking branch 'origin/dev' into HEAD
pettinarip May 8, 2026
868e194
Merge pull request #18034 from ethereum/jsonld-mainentity-ispartof
pettinarip May 8, 2026
7c13caa
Merge pull request #18143 from ethereum/fix/sitemap-noindex-translati…
pettinarip May 8, 2026
145fdb7
Merge staging into dev
pettinarip May 8, 2026
26139a7
11.6.1
pettinarip May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
73 changes: 73 additions & 0 deletions .claude/skills/page-visual-tests/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
name: page-visual-tests
description: Playwright + Chromatic full-page visual tests for ethereum.org. Trigger on "add a page to the visual suite", "the snapshot keeps changing", "chromatic pages", "chromatic playwright", or edits to `tests/visual/`, `playwright.visual.config.ts`, or `.github/workflows/chromatic-pages.yml`. Skip for Storybook Chromatic (`chromatic.yml`), e2e (`tests/e2e/`), unit (`tests/unit/`).
---

# Page Visual Tests (Playwright + Chromatic)

This repo has two Chromatic projects: Storybook (`chromatic.yml` + `pnpm chromatic`) and **page visual tests** (`chromatic-pages.yml` + `pnpm chromatic:pages`). This skill is the second one only.

The Playwright suite captures DOM archives (not PNGs) per page × viewport; Chromatic re-renders them in the cloud to diff. A green local `pnpm test:visual` just means archives were produced — the diff happens after upload.

## Files that matter

- `playwright.visual.config.ts` — visual-only config (3 viewports + `webServer`)
- `playwright.config.ts` — base (e2e + unit; **no `webServer`**)
- `tests/visual/pages.spec.ts` — page list + readiness pattern
- `.github/workflows/chromatic-pages.yml` — CI
- `src/components/ui/skeleton.tsx`, `src/components/ui/spinner.tsx` — loading primitives
- `package.json` scripts: `test:visual*`, `chromatic:pages`

## Non-obvious constraints

**Dual Playwright config.** `webServer` lives only in `playwright.visual.config.ts`. Moving it into the base config breaks `pnpm test:unit` and `pnpm test:e2e` in CI — they try to start Next against a missing `.next` build.

**Desktop viewport is 1024, not 1280.** Chromatic caps snapshots at `width × height ≤ 25M` px. The tallest tested pages reach ~22.5k px; 1280 overflows, 1024 fits. Measure `document.documentElement.scrollHeight` before raising the viewport or adding a long page.

**Loading contract: `data-slot="loading"`.** The shared `Skeleton` and `Spinner` primitives carry this attribute. Each test waits until `document.querySelectorAll('[data-slot="loading"]').length === 0` before snapshotting. Any bespoke loader — raw `animate-pulse-light`, a local Skeleton copy, a custom spinner — is invisible to the wait and will silently flake. Fix by routing through the shared primitive or adding `data-slot="loading"` to the bespoke loader's root.

**Imports come from `@chromatic-com/playwright`, not `@playwright/test`.** The two packages re-export `expect` with skewed types, so `expect(...).toHaveCount(0)` misbehaves — prefer `page.waitForFunction` for the loading wait.

**Environment.** `USE_MOCK_DATA=true` and `NEXT_PUBLIC_BUILD_LOCALES=en` are required at build and test time. Paths in the spec are unprefixed (`/wallets/`, not `/en/wallets/`) because `localePrefix: "as-needed"` serves English at the root — adding `/en` would just trigger a redirect.

**Random ordering: `maybeShuffle`.** Lodash `shuffle` and `.sort(() => Math.random() - 0.5)` flake snapshots independently of loaders. Wrap them with `maybeShuffle` from `src/lib/utils/random.ts` — it returns the list unchanged when `IS_VISUAL_TEST=true`. Current call sites: `wallets.ts`, `apps.ts` (Highlights/Discover/AppOfTheWeek), `useStakingProductsCardGrid.ts`. The env var is exposed to the client bundle via `next.config.js`'s `env` block; without that, `process.env.IS_VISUAL_TEST` evaluates to `undefined` in client components and the shuffle still runs.

**Use `domcontentloaded`, not `networkidle`.** Analytics and background fetches keep the network perpetually busy.

## Canonical test

```ts
import { takeSnapshot, test } from "@chromatic-com/playwright"

const pages: Array<{ name: string; path: string }> = [
{ name: "Homepage", path: "/" },
{ name: "Docs - Smart Contracts", path: "/developers/docs/smart-contracts/" },
// ...
]

test.describe("Page Visual Tests", () => {
for (const { name, path } of pages) {
test(name, async ({ page }, testInfo) => {
await page.goto(path, { waitUntil: "domcontentloaded" })
await page.waitForFunction(
() => document.querySelectorAll('[data-slot="loading"]').length === 0
)
await takeSnapshot(page, testInfo)
})
}
})
```

## Common situations

**Adding a page.** Each entry costs three snapshots (one per viewport) against Chromatic's budget, so check whether the page's layout (under `src/layouts/`) is already covered before adding. Scan the page subtree for bespoke loaders — they're the single biggest flake cause — and confirm full-page height stays under the 25M-pixel budget. Local loop: `pnpm test:visual:build` once, then `pnpm test:visual:desktop` for iteration, `pnpm test:visual` for the full sweep.

**Flaky snapshot.** Two main causes. (1) A loader without `data-slot="loading"` — run with `--trace=on` and inspect the `waitForFunction` step; ~0 ms duration means it isn't being waited on. (2) Random ordering — grep the page subtree for `shuffle(`, `Math.random()`, or `.sort(() =>` and route through `safeShuffle`. If dynamic content is drifting, double-check `USE_MOCK_DATA=true` is set in both build and test steps.

**Local `pnpm dev` masks a regression.** `playwright.visual.config.ts` sets `reuseExistingServer: true`, which is correct for CI but means a `pnpm dev` already running on :3000 will be used silently in place of the production build the suite assumes. If a snapshot diff doesn't reproduce in CI, kill the dev server and run `pnpm test:visual:build` to rebuild against the production output before retrying.

**Pixel-limit error.** Measure the page's full-page height at 1024 px; if it exceeds ~24,400 px, the page needs shortening or removal from the suite. Cropping to viewport was considered and rejected — it defeats the below-the-fold regression coverage that justifies using Playwright over Storybook here.

**Works locally, fails in CI.** Usually `HOME: /root` missing from the test step — GitHub Actions overrides `HOME` inside containers, and Playwright can no longer find the browsers baked into the `mcr.microsoft.com/playwright` image. Also check that the image tag matches `@playwright/test` in `package.json`.

Branch: `feat/playwright-chromatic-page-visual-tests` · PR: <https://github.com/ethereum/ethereum-org-website/pull/18009>
90 changes: 90 additions & 0 deletions .github/workflows/chromatic-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Playwright + Chromatic full-page visual regression tests
# Separate from chromatic.yml (Storybook component snapshots)
name: "Chromatic: Page Visual Tests"

on:
pull_request:
branches: [master, staging, "test/**"]
types: [opened, synchronize, ready_for_review]
workflow_dispatch:

jobs:
playwright-visual:
name: Build & Capture Snapshots
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.53.1-noble
steps:
- name: Checkout repo
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 20
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build Next.js with mock data (English only)
run: pnpm build
env:
USE_MOCK_DATA: "true"
IS_VISUAL_TEST: "true"
NEXT_PUBLIC_BUILD_LOCALES: "en"

- name: Run visual tests
run: pnpm test:visual
env:
HOME: /root
USE_MOCK_DATA: "true"
IS_VISUAL_TEST: "true"

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: chromatic-archives
path: test-results/
retention-days: 1

chromatic-upload:
name: Upload to Chromatic
needs: playwright-visual
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 20
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Download test results
uses: actions/download-artifact@v4
with:
name: chromatic-archives
path: test-results/

- name: Publish to Chromatic
uses: chromaui/action@v16
with:
projectToken: ${{ secrets.CHROMATIC_PAGES_TOKEN }}
playwright: true
exitZeroOnChanges: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/coverage
tests/__results__/
tests/__report__/
test-results/

# next.js
/.next/
Expand Down Expand Up @@ -64,6 +65,7 @@ src/data/crowdin/bucketsAwaitingReviewReport.csv
build-storybook.log
build-archive.log
storybook-static
storybook-static-pages

# Trigger
.trigger
Expand Down
Loading
Loading