Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
# Snapshots are taken by our test suite and used to determine regressions.
*.snap linguist-generated=true

# Visual regression baselines, generated by `pnpm test:visual:update`.
src/renderer/**/__screenshots__/**/*.png binary linguist-generated=true

# GraphQL Codegen outputs
src/renderer/utils/api/graphql/generated/**/*.ts linguist-generated=true
43 changes: 43 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,49 @@ jobs:
name: code-coverage-report
path: coverage/lcov.info

visual-regression:
name: Visual Regression
# arm64 to match `scripts/visual.sh`, which cannot generate amd64 baselines
# because Chromium segfaults under emulation on Apple Silicon.
runs-on: ubuntu-24.04-arm
permissions:
contents: read

# The same image the baselines were generated with. The committed
# screenshots are comparable only against an identical browser build and
# font stack, which a bare runner does not provide.
#
# This tag must match the `playwright` devDependency, which ships the
# matching browser build. Renovate groups the two so they always move
# together; `scripts/visual.sh` reads the version straight from package.json.
container: mcr.microsoft.com/playwright:v1.62.1-noble

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Setup Node.js
uses: ./.github/actions/setup-node

- name: Run visual regression tests
# The job already runs in the pinned image, so the script runs vitest
# directly instead of starting another container.
env:
GITIFY_VISUAL_IN_CONTAINER: '1'
run: pnpm test:visual

# Vitest writes the actual and diff images here; the reference stays in
# __screenshots__. Download this to see what changed.
- name: Archive visual diffs
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: visual-regression-diffs
path: .vitest-attachments/
if-no-files-found: ignore

sonarqube:
name: SonarQube Cloud Analysis
runs-on: ubuntu-latest
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,15 @@ schema.graphql
coverage
*.lcov

# Visual regression screenshots. Only the Linux baselines CI compares against
# are committed; anything generated on a developer's own OS would never match
# it (font rendering differs) and must not become a baseline. Regenerate with
# `pnpm test:visual:update`, which runs in the pinned Linux container.
src/renderer/**/__screenshots__/**/*.png
!src/renderer/**/__screenshots__/**/*-chromium-linux.png

# Actual/diff images written by a failing visual regression run
.vitest-attachments/

# Mac Files
.DS_Store
32 changes: 32 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,38 @@ pnpm test
pnpm test -u
```

#### Visual regression tests

`pnpm test` covers the DOM snapshots. Pixel-level regressions (colour tokens,
spacing, theme changes) are covered separately by a browser-mode project that
screenshots every route across the light, dark, accessibility, and Glass themes.

Screenshots only match when the browser build and font stack are identical, so
both commands below run the suite inside a pinned Playwright container, and CI
verifies against that same container. You do not need to install Playwright's
browsers locally.

```shell
# Verify against the committed baselines
pnpm test:visual

# Regenerate them after an intentional UI change
pnpm test:visual:update
```

Both need Docker on an arm64 host, because Chromium crashes under amd64
emulation on Apple Silicon. On any other machine, push the branch and read the
diff from the Visual Regression job's `visual-regression-diffs` artifact.

Do not invoke the `browser [visual]` project through `vitest` directly. Vitest
namespaces baselines per platform and creates missing ones automatically, so a
bare run on macOS writes a fresh set from your working tree and then passes
against it — reporting green even on a branch that has a real regression. A
guard fails the run outside Linux rather than letting that happen.

Note that these baselines capture the `backdrop-filter` fallback for Glass, not
the macOS native vibrancy material, which Chromium cannot render.

### Code Style & Conventions

- Linting and formatting are configured in `vite.config.ts` (the `lint` and `fmt` blocks). Please run `pnpm check` before submitting a PR.
Expand Down
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@
"lint:check": "vp lint",
"format": "vp fmt",
"format:check": "vp fmt --check",
"test": "vp test --coverage --run",
"test": "vp test --coverage --run --project 'happy-dom [preload, renderer]' --project 'node [main, shared]'",
"test:watch": "vp test --watch --coverage",
"test:ui": "vp test --ui"
"test:ui": "vp test --ui",
"test:visual": "scripts/visual.sh",
"test:visual:update": "scripts/visual.sh --update"
},
"dependencies": {
"electron-log": "5.4.4",
Expand Down Expand Up @@ -107,6 +109,8 @@
"@types/react-router-dom": "5.3.3",
"@types/semver": "7.8.0",
"@vitejs/plugin-react": "6.0.5",
"@vitest/browser": "4.1.10",
"@vitest/browser-playwright": "4.1.10",
"@vitest/coverage-v8": "4.1.10",
"clsx": "2.1.1",
"concurrently": "10.0.4",
Expand All @@ -118,6 +122,7 @@
"graphql": "17.0.2",
"happy-dom": "20.11.1",
"husky": "9.1.7",
"playwright": "1.62.1",
"react-devtools": "7.0.1",
"rimraf": "6.1.3",
"semver": "7.8.5",
Expand Down
Loading