Repository navigation
v6.0.0
The VRT web UI 6.0.0: a redesigned interface with dark mode, a post-analysis rendering-noise hint, undo for review decisions, and React 19, MUI 9 and Vite 8 underneath.
- Image:
visualregressiontracker/ui:6.0.0(linux/amd64andlinux/arm64) - Requires API 6.0.0: https://github.com/Visual-Regression-Tracker/backend/releases/tag/api-v6.0.0
- User overview and upgrade steps (PostgreSQL 18,
JWT_SECRET, compose files): https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/releases/tag/6.0.0
⚠️ Breaking changes
- UI 6.0 needs API 6.0. It calls
POST /test-runs/revert,GET /projects/favorites,PUT|DELETE /projects/:id/favorite,GET /statistics/results,GET /dinov2/statusand reads per-run results of bulk approve, reject and delete. Against an older API, Undo and starring answer 404. - Live updates need a login. The socket connects with
auth: { token }and sendsjoin/leavefor the open project ({ projectId }) and build ({ buildId }), again on every reconnect. A server that closes the socket (io server disconnect, for example an expired token or a deleted user) logs the user out; client-side disconnects and transport drops do not. - Node 26 (
engines.node >=26,.nvmrc26) is required to build the UI from source. - Image base:
nginx:1.30-alpine-slim(wasnginx:1.25-alpine3.17-slim); the builder stage isnode:26-alpine, run on the build platform. Runtime configuration is unchanged:env.shwritesenv-config.jsfromREACT_APP_API_URL,REACT_APP_STATIC_URL,REACT_APP_OIDC_AUTO_REDIRECT,VRT_VERSIONandPORT(nginx still listens on 8080). - Project settings keep two post-analysis switches. The UI saves
noiseAnalysisConfigas{ "blur": { "enabled": … }, "dinov2": { "enabled": … } }; thresholds stored by an earlier release candidate are dropped on the next save. New projects created from the UI start with both switches off; a stored config withoutenabledreads as off. - Removed UI features: see Removed.
- Changed locators. Status chips and counters show sentence-case labels (
Passed,Auto-approved,Running) instead of enum values; enum values stay in API calls, URLs, storage anddata-testids.src/constants/help.tsbecamesrc/constants/locators.ts. The project switcher label id isproject-select-label. Custom Playwright suites that match these texts or ids need updating.
New
- Undo approval / Undo rejection in the run dialog footer, and an Undo button on the toasts after approve or reject (run dialog, bulk actions, matching-variations dialog). Calls
POST /test-runs/revert?decision=approve|reject; a409(status changed meanwhile) shows the server's message. A bulk action that partly failed offers Undo only for the runs that went through. Guests never see Undo. - Refused decisions warn: a run the server refuses (already approved, changed by someone else) shows an orange warning with the reason;
Internal errorin a bulk result shows as an error. Toasts name the run instead of its id. - Favourites: a star on each project card; starred projects are listed first, in the list and in the project switcher. Kept per user by the API (
404fromGET /projects/favoritescounts as no favourites). - Project list search by name, id or main branch; the query stays in the URL (
?q=) and/focuses the field. The project switcher is an autocomplete with the same groups and search. - Project page with tabs: Builds, Variations, Statistics, Settings, with a breadcrumb; an unknown project shows "Project not found" with a link back.
- Statistics tab: results per build (
GET /statistics/results), with a branch filter. - Run list: filter chips, selection bar, table or grid view, grid "Sort by" with select all, comments visible in rows and cards.
- Run dialog: header and info bar; view modes Side by side, Diff and Fade; ignore-area edit mode; images refit when the panes resize until you zoom by hand (Fit re-arms it).
- Rendering-noise hint (post-analysis):
- A Noise column with a verdict chip per unresolved run (
Likely change,Likely noise,Analysing…) and a noise panel in the run dialog with Analyse / Analyse again. - Analyse noise from the build menu, the run list selection bar (queued and skipped runs are reported; queued rows flip to "Analysing…" at once) and the run's noise chip.
- The column, chips and actions show only when the project has at least one post-analysis stage enabled.
- Project settings, section Post-analysis: blur and DINOv2 switches; DINOv2 is disabled when the server has no DINOv2 service (
GET /dinov2/status). - The hint never changes a status.
- A Noise column with a verdict chip per unresolved run (
- Build header with status counters and "Analysing N of M…" progress.
- Variations: list with filters; variation page with baseline history.
- Add project page; project settings in sections.
- Profile page with API key regenerate (confirms first).
- Users page: search, role filter, selection bar, joined date; deleting users asks for confirmation first.
- Help menu has a Documentation link to the GitHub Wiki.
Redesign & theming
- Design tokens in
src/theme/tokens.ts: primary is the logo's cyan on slate neutrals, Inter bundled with the app (no CDN), softer radius and shadows. - Dark mode follows the system and remembers an explicit choice.
- Status colours come from one theme source (
statusColor) in light and dark; a test keeps their contrast at WCAG AA in both modes. - Dialogs share one look:
BaseModaltitle and close button, Cancel left of a filled primary action, destructive confirms in the error colour. - Shared building blocks:
StatusChip,FilterBar,SelectionBar,TagLine, short dates, outlined fields, empty states, inline validation messages. @mui/stylesis gone; styling usessxandstyled()with theme values.
Performance
- Routes are lazy-loaded; the run dialog (Konva) and
jszipload on demand. In #434 the main chunk went from 1.6 MB to 137 KB. npm run check:sizeenforces a gzip budget in CI: initial load (entry plus preloaded chunks) 287 kB andProjectPage22 kB.- Grids and the variations dialog draw the small ingest thumbnails (
loading="lazy"); the run dialog prefetches the images of neighbouring runs. - Memoised dialog work and stable Data Grid props; a large-build perf spec (
PW_PERF=1, own CI job) measures the cost of socket updates. - Run and build lists no longer duplicate or lose entries on socket events or late answers; a late
testRun_creatednever overwrites a newertestRun_updated, and an update for a deleted run does not bring it back.
Live updates & API integration
- Socket handshake sends the user's token;
join/leavefollow the selected project and build, and are re-sent onconnect. Events are listened to one by one, so the rooms'connectlistener stays. - Server-initiated disconnect logs out (see Breaking changes).
- Error handling (
handleResponse):- non-2xx answers reject with the body's
message, falling back to the status text; - a body that is not JSON (the proxy's HTML page while the API restarts) no longer throws
SyntaxError; a401still logs out, and the error is always a string; - after login you return to the full link, query included.
- non-2xx answers reject with the body's
- Bulk approve, reject and delete show partial results and use each run's own merge flag; an empty answer body counts as all succeeded.
- Registration flag:
404from the API counts as enabled. The Gemini API key is shown as a masked hint. - Stale-answer guard (
staleGuard) keeps only the latest request for a run, build or build list.
Removed
- Merge branches panel. The API endpoint stays.
- Guided tour (
react-joyride,GuidedTour*, "Take a tour" in the Help menu); the Help menu links to the documentation instead. - Cut before 6.0.0 (these existed only in 6.0 release candidates, not in 5.6.0):
- variation grouping in the run list ("Group by"), the View menu, tile density and the grid's diff toggle (cards always show the diff thumbnail when there is one);
- the "Recently opened" projects group (favourites, then other projects);
- the difference blend of the Fade view (Fade stays);
- the browser-side Diff without noise view and the noise hint's Blur / DINOv2 area overlays;
- the run list Noise filter and noise sort (the Noise column stays, unsortable);
- post-analysis threshold fields (amplitude, solid share, distance);
- the build-level noise caption and the "runs look like a renderer change" banner.
Fixed
- Bulk delete, clear-ignore-areas and download acted on the selection of another build; they now send only selected runs that are shown, and the selection clears when another build opens.
- Drawn, unsaved ignore areas were wiped by any update to the open run (a comment, another reviewer); a failed save no longer clears the "unsaved" flag.
- Ignore areas stay inside the image.
- The images zip overwrote files of the same test in several browsers or viewports; names now include the tags, repeats are numbered.
- Left arrow on the first run opened the last one;
A/Xused stale callbacks. - Escape closes the run dialog (still asking about unsaved ignore areas); Escape inside the comment box closes only the box. Dialog hotkeys keep working after clicking the diff switch.
- A failing comment save keeps the box open with the draft.
- Late answers replaced current ones (run or build opened from a URL, build list after switching projects or searching).
- Tag filter: a value shared by several fields matches in any of them.
- Saving a compare area inverts the selected area and warns when none is selected, instead of storing
[]. - Icon buttons carry their own accessible names; the
Tooltipwrapper accepts onlytitleandchildren. - Guests no longer see project create, edit and delete controls.
- Opening a variation full size in the matching-variations dialog blanked the page in production builds (React #130).
- A role the server rejects reverts in the user list; a failed user load shows an error.
window.openfor "Open an issue in GitHub" passesnoopener,noreferrer.- Dark theme: Data Grid borders follow the theme divider; secondary colour is a neutral slate and Reject is error-coloured.
Stack & tooling
| Package | 5.6.0 | 6.0.0 |
|---|---|---|
Node (engines) |
>=20 |
>=26 |
| React / React DOM | 18.3 | 19.3 |
react-konva |
18.2 | 19.3 |
konva |
7.2 | 10.9 |
@mui/material |
5.18 | 9.5 |
@mui/x-data-grid |
6.20 | 9.17 |
react-hotkeys-hook |
4.4 | 5.3 |
react-router-dom |
7.18.4 | 7.18.5 |
@vitejs/plugin-react |
6.1 | 6.2 |
| TypeScript | 5.2 | 6.0 |
| ESLint | 8 (.eslintrc.cjs) |
9 (flat config) |
Jest 30 + ts-jest |
yes | replaced by Vitest 5.1 |
@testing-library/react |
14 | 16 |
husky |
8 (inactive v4-style block) | 9 (pre-commit hook) |
react-joyride |
2.5 | removed |
@mui/styles, react-debounce-input, react-material-ui-form-validator |
yes | removed (sx/styled, DebouncedTextField, own form validation) |
| nginx runtime image | 1.25 (Alpine 3.17) | 1.30 (alpine-slim) |
| Vite, Playwright, socket.io-client | 8.3.5, 1.64.1, 4.8.4 | unchanged |
- Unchanged:
vite build; output is static files served by nginx. - Hotkeys follow the physical key (
react-hotkeys-hook5): letter hotkeys sit on the same key position on non-Latin layouts and move on AZERTY. - ESLint enforces the import flow (
pages → components → contexts → services → types),fetchonly insrc/services, no MUI internals;eslint-plugin-sonarjsruns innpm run lint;knip(npm run knip) and the bundle-size check run in CI. minimatch10.x consumers are overridden to 11.0.5 (GHSA globparts DoS, dev-time tooling only).- Docker image: published for
linux/amd64andlinux/arm64by.github/workflows/docker.ymlon avX.Y.Ztag; the builder runs once on the build platform. - CI: lint, typecheck, unit tests with coverage, build, bundle-size check, Playwright (functional, Docker screenshots, perf) and the E2E smoke; actions are SHA-pinned; Dependabot covers npm, Docker and GitHub Actions.
- Agent and contributor rules:
AGENTS.md,CLAUDE.md,CONTRIBUTING.md,docs/TESTING.md.
Testing
- Vitest replaces Jest: unit and component tests next to the code,
renderWithProviders, coverage thresholds invitest.config.tsthat only go up. - At the final merge: 1635 unit tests and 502 Playwright tests passing (as reported in #502). At 5.6.0 there were 47 unit tests and 123 Playwright tests.
- Visual contract:
integration_tests/test/screens.spec.tsshoots every view and notable state in light and dark; baselines are produced only in Docker (npm run test:pw:docker,npm run test:pw:docker:update). - Specs use page objects only (
integration_tests/pages); API mocks inintegration_tests/utils/mocks.ts. - E2E smoke against the real backend stack (
PW_E2E=1 npx playwright test,integration_tests/e2e): seeded admin login, a run posted through the API is approved, a run posted to the open build arrives over the socket; CI runs it also with the API in strict socket mode. - Perf spec (
PW_PERF=1,integration_tests/perf): a large build with socket events. - Definition of done:
npm run lint,npm run typecheck,npm test,npm run build,npm run test:pw:docker.
What's Changed
- fix(projects): hide project create, edit and delete from guests by @pashidlos in #430
- ci(docker): build and publish the ui image for amd64 and arm64 by @pashidlos in #431
- fix(ignore-areas): keep ignore areas inside the image by @pashidlos in #432
- Modernize the frontend: tests, MUI 7, agent rules, new theme with dark mode by @pashidlos in #434
- test(e2e): cover SSO login, user deletion, the build menu and login errors; fix the build menu dialogs by @pashidlos in #435
- fix: Escape and hotkeys in test details, merge branch encoding, failing comment save by @pashidlos in #436
- fix: bugs from the frontend review — wrong runs in bulk actions, lost ignore areas, stale answers by @pashidlos in #438
- refactor: address SonarCloud findings on #438 by @pashidlos in #439
- feat(project-form): rendering-noise filter in each library's config by @pashidlos in #440
- feat(project-form): DINOv2 check under the rendering-noise filter by @pashidlos in #441
- feat(projects): favourites, recently opened and search in the project list and switcher by @pashidlos in #442
- Frontend refactoring, review and live-update fixes, joint e2e smoke by @pashidlos in #444
- feat(test-runs): undo an approval or rejection from its toast (U5) by @pashidlos in #446
- feat(test-variations): merge branches with POST /test-variations/merge (A1) by @pashidlos in #447
- feat(socket): join the open project's and build's rooms (A3) by @pashidlos in #448
- build(deps): safe updates for 6.0 — Node 26, nginx 1.30, pinned actions by @pashidlos in #449
- refactor: behaviour-neutral cleanup before 6.0.0 by @pashidlos in #450
- ci: run the e2e backend stack with a generated JWT_SECRET by @pashidlos in #455
- chore: require Node 26 by @pashidlos in #454
- feat(project-form)!: one "Ignore rendering noise" switch by @pashidlos in #456
- feat(socket): log out when the server closes the socket by @pashidlos in #457
- refactor: move off GridLegacy to the MUI 7 Grid by @pashidlos in #459
- refactor: ready for MUI 9 — slotProps, sx, own form validation by @pashidlos in #461
- refactor: split TestDetailsModal and TestRunList under 300 lines by @pashidlos in #463
- chore(lint): warnings become errors, strict integration_tests by @pashidlos in #464
- fix: tag filter across fields, compare area, icon button names by @pashidlos in #465
- refactor: minimal 6.0 cleanup by @pashidlos in #466
- docs: AGENTS.md without library versions by @pashidlos in #467
- fix: undo sends the decision it reverts; refused decisions warn by @pashidlos in #468
- feat: rendering-noise hint on unresolved runs, replacing the noise filter by @pashidlos in #469
- F0: shared building blocks (StatusChip, FilterBar, SelectionBar, TagLine, short dates) by @pashidlos in #470
- feat(noise): browser solid-mask core for Diff without noise (P6a) by @pashidlos in #471
- feat: project shell (breadcrumb, tabs, Settings, Statistics) by @pashidlos in #472
- feat: Statistics tab with results per build (F8) by @pashidlos in #473
- feat: build list rows, selection bar and build header with status counters and noise line by @pashidlos in #474
- feat: project list redesign (Copy id, settings icon, search hint) by @pashidlos in #475
- feat: users page with search, role filter, selection bar and joined date by @pashidlos in #476
- feat: project settings as sections (F14) by @pashidlos in #477
- feat: variations list and baseline history (F9, F10) by @pashidlos in #478
- feat: test-run dialog header, info bar and noise chip (F5a) by @pashidlos in #479
- feat: run list filter chips, View menu and selection bar (F4) by @pashidlos in #481
- feat: profile page redesign (F13) by @pashidlos in #482
- feat: test-run dialog view modes, area editing and footer undo (F5b, F6) by @pashidlos in #483
- feat: Diff without noise view in the test-run dialog (F7 UI) by @pashidlos in #484
- test: reconcile tour, screens, coverage and thresholds (F12) by @pashidlos in #485
- build: override minimatch 10.x to 11.0.5 (Dependabot alert 185) by @pashidlos in #486
- Show sentence-case labels for run and build statuses by @pashidlos in #487
- fix: redesign tails by @pashidlos in #488
- chore: remove the guided tour by @pashidlos in #489
- Clearer project switcher groups and visible comments by @pashidlos in #490
- test: noise column and filter hidden when the noise check is off by @pashidlos in #491
- feat: bulk Analyse rendering noise for selected runs by @pashidlos in #492
- feat: noise analysis off by default, documentation link in help menu by @pashidlos in #493
- feat: consistent page layout and an Add project page by @pashidlos in #494
- feat: restyle dialogs to the 6.0 look by @pashidlos in #495
- refactor: split oversized files, share stale-answer guard, theme overlay colours by @pashidlos in #496
- test: specs use page objects only by @pashidlos in #497
- fix: UI consistency from the rc7 review by @pashidlos in #498
- chore: socket rooms comment without the removed WS_BROADCAST flag by @pashidlos in #499
- refactor!: remove the build-level noise caption and renderer-change banner by @pashidlos in #500
- refactor!: cut Diff without noise, noise hint areas, filter and sort, post-analysis thresholds by @pashidlos in #501
- refactor!: cut variation grouping, tile density/diff toggle, recent projects and the Fade blend by @pashidlos in #502
- Dependency updates (konva 9.x #433, React 19 with react-joyride 3 and react-konva 19 #437, npm minor/patch group #445, GitHub Actions group #452, Data Grid 9 / konva 10 / TypeScript 6 / react-hotkeys-hook 5 #458, MUI 9 #462) by @pashidlos and @dependabot in #433, #437, #445, #452, #458, #462
Full Changelog: v5.6.0...v6.0.0