Here is where visual testing gets tricky. You generate baselines on your Mac. CI runs on Linux. Font rendering is different. Anti-aliasing is different. Your screenshots will never match. Every single visual test fails in CI. This is the number one complaint about visual testing. But there is a clean solution: Docker.
The same HTML renders differently on macOS, Linux, and Windows. Fonts have different hinting. Sub-pixel rendering varies. Even the same font at the same size produces different pixels. Playwright names snapshots with the OS to avoid mixing them, but that means you need separate baselines for each OS.
Run Playwright in Docker both locally and in CI. Same Linux image. Same fonts. Same rendering. Screenshots match perfectly.
# Run Playwright tests inside the official Docker image
docker run --rm -v $(pwd):/work/ -w /work/ \
mcr.microsoft.com/playwright:v1.49.0-noble \
npx playwright test --update-snapshots
# Now run tests (same Docker image, same rendering)
docker run --rm -v $(pwd):/work/ -w /work/ \
mcr.microsoft.com/playwright:v1.49.0-noble \
npx playwright testname: Visual Tests
on: [push, pull_request]
jobs:
visual-tests:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.49.0-noble
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run visual tests
run: npx playwright test tests/visual/
- name: Upload test results on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: visual-test-results
path: test-results/
retention-days: 7# Visual test output (temporary, generated on each run)
test-results/
playwright-report/
# Do NOT ignore snapshots -- they are your baselines
# tests/**/*-snapshots/ ← Do NOT add this linename: Update Visual Baselines
on:
workflow_dispatch: # Manual trigger only
jobs:
update:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.49.0-noble
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Update snapshots
run: npx playwright test --update-snapshots
- name: Commit updated snapshots
run: |
git config user.name "github-actions"
git config user.email "actions@github.com"
git add "*.png"
git commit -m "Update visual baselines" || echo "No changes"
git pushAlways use the same Playwright Docker image version in your CI and for local baseline generation. If the image version changes, font rendering might change, and all your baselines will break. Pin the version in both places.
Run visual tests in a separate CI job from functional tests. Visual tests are slower and more sensitive. Separating them lets you rerun only visual tests when they fail, without re-running the entire suite.
Key Point: Use Docker for consistent rendering across local and CI. Same image, same fonts, same pixels. Store snapshots in git, temporary results in .gitignore.