Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cfb27f0
chore: green baseline (typecheck, lint, husky 9, CI gates)
Oct 24, 2026
6724992
docs: modernization plan, revised after two reviews
Oct 24, 2026
a634232
test: move unit tests from Jest to Vitest
Oct 24, 2026
35c1e99
docs: rules for contributors and coding agents
Oct 24, 2026
dd9aeda
fix(types): Build.updatedAt is optional, as the UI already treats it
Oct 24, 2026
c897554
refactor: replace @mui/styles with sx and styled
Oct 24, 2026
ee50568
test: cover services, helpers and context reducers
Oct 24, 2026
96be493
build: ESLint 9 flat config that enforces the project layout
Oct 24, 2026
e4fdafd
perf: load pages on first visit
Oct 24, 2026
dadb744
test: component tests for lists, dialog controls and socket updates
Oct 24, 2026
70fb168
test(e2e): project form engines, ignore areas, comments, hotkeys, mat…
Oct 24, 2026
09458f9
fix: the full-size variation view no longer blanks the page
Oct 24, 2026
6b12b16
test: component tests for forms and debounced inputs
Oct 24, 2026
8f99982
test: harden two new e2e tests; tests survive a loaded machine
Oct 24, 2026
0c15e2a
build(deps): MUI X Data Grid 6 to 8
Oct 24, 2026
3096a92
refactor: DebouncedTextField replaces react-debounce-input
Oct 24, 2026
31e833e
build(deps): MUI 7 and react-material-ui-form-validator 5
Oct 24, 2026
60bc74f
test: enforce the coverage reached (51% lines, from 6.5%)
Oct 24, 2026
493c4fe
feat(ui): new MUI theme with dark mode and refreshed screens
Oct 24, 2026
73f8431
docs(AGENTS): Grid, slotProps and icon test id rules learned in the M…
Oct 24, 2026
dd8df02
fix: address the branch review
Oct 24, 2026
7188886
ci: cancel superseded PR runs, cache npm for the Docker e2e run; fix …
Oct 24, 2026
09c51d2
ci: name the Docker volume with SHA-256 (SonarCloud S4790)
Oct 24, 2026
9f1aa45
fix: address the code review of #434
Oct 24, 2026
27ba875
test(e2e): a screenshot of every view, in light and dark
Oct 24, 2026
bfaeb21
feat(ui): second redesign pass — variations, profile, users, empty st…
Oct 24, 2026
3f8202d
test(e2e): fit #435's new tests to MUI 7 and the redesign
Oct 24, 2026
41b65c2
fix: clear the SonarCloud issues on #434
Oct 24, 2026
57d3f52
feat(ui): rework the dark theme on neutral graphite
Oct 24, 2026
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
29 changes: 29 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm test)",
"Bash(npm run test:*)",
"Bash(npm run lint)",
"Bash(npm run typecheck)",
"Bash(npm run build)",
"Bash(npx vitest run:*)",
"Bash(npx playwright test:*)",
"Bash(npx prettier --write:*)",
"Bash(npx tsc --noEmit:*)"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path // empty' | grep -E '\\.(ts|tsx|js|jsx|json|css|md)$' | xargs -r npx prettier --write --log-level warn"
}
]
}
]
}
}
30 changes: 0 additions & 30 deletions .eslintrc.cjs

This file was deleted.

13 changes: 13 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## What and why

<!-- One or two sentences: what changes for the user, and why. -->

## How it was checked

- [ ] `npm run lint` and `npm run typecheck`
- [ ] `npm test` (new behaviour has a test that failed before)
- [ ] `npm run test:pw:docker` (screenshots regenerated in Docker only, every changed PNG looked at)

## Screenshots

<!-- Before / after for anything visible. -->
20 changes: 17 additions & 3 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,35 @@ on:
pull_request:
branches: [master]

# a new push to a pull request makes the run for the previous one moot;
# runs on master always finish
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
test:
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc

- uses: actions/setup-node@v3
# npm's download cache, which the container's npm ci reuses
- uses: actions/cache@v4
with:
node-version: 20
path: ${{ runner.temp }}/npm-docker
key: npm-docker-${{ hashFiles('package-lock.json') }}
restore-keys: npm-docker-

- name: Run Playwright tests
run: npm run test:pw:docker
env:
CI: true
NPM_CACHE_DIR: ${{ runner.temp }}/npm-docker

- uses: actions/upload-artifact@v4
if: always()
Expand Down
24 changes: 20 additions & 4 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,36 @@ on:
pull_request:
branches: [master]

# a new push to a pull request makes the run for the previous one moot;
# runs on master always finish
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Setup Node.js environment
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: .nvmrc
cache: npm

- name: Install npm dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Unit tests
run: npm test -- --coverage
run: npm run test:coverage

- name: Build
run: npm run build
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ yarn-error.log*
/test-results/
/playwright-report/
/playwright/.cache/

# Claude Code
.claude/settings.local.json
.claude/worktrees/
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
104 changes: 104 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Working on the VRT frontend

Rules for anyone changing this repo, people and coding agents alike. The
frontend of [Visual Regression Tracker](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker):
React 18, TypeScript (strict), Vite, MUI 7 with MUI X Data Grid, Konva for
the image canvas, socket.io for live updates.

## Commands

| What | Command |
| ----------------------------------------------------- | --------------------------------------------------------- |
| Dev server (needs `.env`, API on `REACT_APP_API_URL`) | `npm start` |
| Unit and component tests (Vitest) | `npm test`, `npm run test:watch`, `npm run test:coverage` |
| Lint / types | `npm run lint`, `npm run typecheck` |
| Production build | `npm run build` |
| Playwright, functional, on your machine | `npx playwright test --grep-invert renders` |
| Playwright, everything incl. screenshots (Docker) | `npm run test:pw:docker` |
| Regenerate screenshot baselines (Docker only) | `npm run test:pw:docker:update` |

## Definition of done

A change is ready when all of these pass: `npm run lint`, `npm run typecheck`,
`npm test`, `npm run build`, `npm run test:pw:docker`. CI runs the same.
Coverage thresholds in `vitest.config.ts` only go up: raise them when you add
tests, never lower them to get green.

## Layout

```
src/
pages/ one file per route, composes components; routes in Router.tsx
components/ UI; a folder when a component has parts (TestRunList/, TestDetailsDialog/)
contexts/ React context + reducer per domain (user, project, build, testRun, socket, help)
services/ the only place that talks HTTP (fetch); one file per API resource
_helpers/ pure functions and small hooks, no JSX
constants/ routes, guided-tour locators and steps, defaults
theme/ MUI theme, design tokens, colour mode
types/ API types, shared enums
_test/ test fixtures and renderWithProviders
integration_tests/
test/ Playwright specs (*.spec.ts) and their screenshot baselines
pages/ page objects; specs use these, not raw selectors
utils/mocks.ts page.route mocks of the API
fixtures/ Playwright fixtures (signed-in user, page openers)
```

Imports flow one way: `pages → components → contexts → services → types`.
`services`, `types`, `_helpers` and `constants` never import from
`components`, `pages` or `contexts`. Lint enforces it, along with `fetch`
outside `src/services`. The one known cycle, `_helpers/service.helpers` ↔
`services`, is old and documented; do not add new ones.

## Conventions

- Styling: the `sx` prop or `styled()` from `@mui/material/styles`, with
values from the theme (`"primary.main"`, `theme.spacing`). No
`@mui/styles`/`makeStyles`, no new global CSS, no hard-coded colours:
add a token in `src/theme/tokens.ts` if one is missing.
- Status colours come from the theme (`statusColor` in `src/theme`), never a
per-component palette, so light and dark stay consistent.
- Data: components call context actions or services; services return typed
promises and go through `handleResponse`.
- Strings shown to users are English, sentence case.
- Layout: `Stack`, `Box` and the MUI 7 `Grid` in new code. Older screens
import `GridLegacy as Grid` to keep their exact layout; move a screen to
the new components when you rework it, not as a drive-by.
- MUI 7 ignores some deprecated props when the new ones are set (a Switch's
`inputProps` loses to `slotProps.input`), so use `slotProps` in new code.
- Icons only carry `data-testid="<Name>Icon"` in development builds. E2E
runs on the production bundle, so an icon a page object finds sets it
explicitly.
- Keep files focused; split a component when it passes ~300 lines rather than
growing it.

## Tests

- `Foo.tsx` → `Foo.test.tsx` next to it (Vitest + Testing Library). Render
with `renderWithProviders` from `src/_test/render.tsx`, mock services with
`vi.spyOn(service, "fn")`, query by role, label or test id, assert what the
user sees and the exact service call. No snapshot tests.
- Playwright specs live in `integration_tests/test` and mock the API with
helpers from `integration_tests/utils/mocks.ts`; nothing there talks to a
real backend. Test names say what the user gets, in plain English.
- `integration_tests/test/screens.spec.ts` is the visual contract: every
view and notable state, in light and dark. A new screen, dialog or state
gets an entry there in the same change.
- A behaviour change needs a test that would have failed before it. A bug
fix starts with the failing test.

## Things that look free to change but are not

- `data-testid`s, `aria-label`s, element ids and visible button texts are
used by page objects and by the guided tour (`src/constants/help.ts`,
`*.locators.ts`). Renaming one means updating the page object and the tour
in the same change.
- Page objects also match some stock MUI class names (`.MuiCard-root`,
`.MuiDataGrid-row`, `.MuiChip-label`, `.notistack-MuiContent`); keep those
elements as the same MUI components.
- Screenshot baselines are produced only in Docker (fonts differ on macOS).
Never commit baselines made on your machine. When a change moves pixels on
purpose, regenerate in Docker and look at every changed PNG before
committing.
- `react-joyride` is capped at React 18 and `react-konva` 19 needs React 19,
so React stays on 18 until both are migrated together.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Contributing

Thanks for helping. The short version:

1. `nvm use` (Node 22), `npm ci`, copy `.env` and point `REACT_APP_API_URL` at a running [backend](https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker).
2. `npm start` for the dev server.
3. Read [AGENTS.md](AGENTS.md): project layout, conventions, how tests are organised, and what must not be renamed. It applies to people as much as to coding agents.
4. Before opening a PR run `npm run lint`, `npm run typecheck`, `npm test` and `npm run test:pw:docker`. The pre-commit hook formats staged files with Prettier.
5. Keep PRs to one topic and fill in the template. Visible changes need before/after screenshots.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### STAGE 1: Build ###
# This image is around 50 megabytes
# The built ui is static files, so build it once on the native platform for every target platform
FROM --platform=$BUILDPLATFORM node:20-alpine AS builder
FROM --platform=$BUILDPLATFORM node:22-alpine AS builder

# Create app directory
WORKDIR /app
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
- Update `.env`
- `npm run start`

The testing related `.spec.tsx` files are used with Playwright for browser tests, and the `.test.tsx` files with Jest for unit tests.
The `.test.tsx` files next to the source are Vitest unit and component tests; the `.spec.ts` files in `integration_tests/test` are Playwright browser tests.

Project layout, conventions and the definition of done are in [AGENTS.md](AGENTS.md); see also [CONTRIBUTING.md](CONTRIBUTING.md).

## Local OIDC (SSO)

Expand All @@ -21,12 +23,11 @@ docker compose -f docker-compose.yml -f docker-compose.oidc.yml up

See the backend README for the local mock identity provider.


## Running Tests

### Unit Tests (Jest)
### Unit and component tests (Vitest)

Run Jest unit tests:
Run them once, or in watch mode with `npm run test:watch`:

```bash
npm run test
Expand Down
Loading
Loading