Skip to content

fix: bugs from the frontend review — wrong runs in bulk actions, lost ignore areas, stale answers - #438

Merged
pashidlos merged 3 commits into
masterfrom
claude/frontend-review-improvements-54c2a1
Oct 24, 2026
Merged

pashidlos merged 3 commits into
masterfrom
claude/frontend-review-improvements-54c2a1

Conversation

@pashidlos

Copy link
Copy Markdown
Member

What and why

A review of the frontend turned up bugs where a reviewer could lose work, act on runs they could not see, or see a run other than the one in the URL. Each was checked against the code first; each fix starts with a test that failed before it (38 new tests). Security, socket reconnect, cross-tab logout and performance were left out on purpose: none of them is a small change.

Losing work or acting on the wrong runs

  • Bulk delete reached runs of another build. The run list stays mounted between builds and kept the selection; delete, clear-ignore-areas and download sent it as is. The selection is now cleared when another build opens, and those actions send only selected runs that are shown (approve/reject already did). The confirmation counts what will be sent.
  • Drawn, unsaved ignore areas were wiped by any update to the open run (a comment, another reviewer), because the reset effect depended on the whole run object. It now depends on the run id and its saved areas.
  • A failed ignore-area save cleared the "unsaved" flag, so leaving the run dropped the areas without asking. The flag is cleared only after a successful save.
  • The zip overwrote images of the same test in several browsers/viewports. Files are named by test and tags, and repeated names are numbered.

Navigation and stale answers

  • ← on the first run opened the last one (Array.at(-1)); a run outside the list no longer moves with the arrows.
  • A/X kept a stale callback (react-hotkeys-hook memoises by deps), so they ignored a freshly ticked "go to next" and skipped the unsaved-areas prompt.
  • Late answers replaced current ones: a run or build opened from the URL, and the build list after switching projects or searching. Each keeps only the latest request; a deleted run/build link now closes cleanly instead of an unhandled rejection.

Smaller

  • A non-JSON error body (the proxy's HTML page while the API restarts) threw a SyntaxError: a 401 did not log out and the toast lost its error style. Parsing is now guarded and the rejection is always a string.
  • Login after an expired session returns to the full link, query included (?buildId=&testId=).
  • A role the server rejects reverts in the user list; a failed user load shows an error.
  • "Apply ignore area to the whole build" no longer hangs on a failure.
  • Canvas keydown listeners no longer pile up (Delete ran twice).
  • No trailing space in VRT_PROJECT on the profile page; a failed login right after registering shows the error.

Also: the tag field list moved to testRunTags.helper (shared by the list and the zip names); coverage floors raised to the reached level (lines 52 → 73).

Not covered: SSO login still lands on the home page rather than the link the user asked for (the target would have to survive the redirect to the provider); a failed fetch still reaches callers as a raw TypeError.

How it was checked

Merged with master after #437, so everything runs on React 19.

  • npm run lint (0 errors) and npm run typecheck
  • npm test: 602 pass, coverage 73% statements / 74% lines
  • npm run test:pw:docker: 285/298 in the full run on a host with load average ~60; the 13 failures were timeouts opening pages and the details dialog. Rerun of those four specs with 2 workers: 68/69; the remaining one (zoomToolbar "shows the screenshots at their own size", the ?testId= dialog-open timeout build(deps): React 19, with react-joyride 3 and react-konva 19 #437 describes on master) then passed 10/10 with --repeat-each=10. No screenshot baselines changed.

Screenshots

Nothing visible changed.

🤖 Generated with Claude Code

Pavlo Strunkin and others added 3 commits October 24, 2026 16:05
… ignore areas, stale answers

Test details
- the left arrow on the first run no longer jumps to the last one
- A/X hotkeys use the current "go to next" setting and unsaved-areas check
- a failed ignore-area save keeps the areas marked unsaved
- an update to the open run (a comment, another reviewer) no longer wipes
  drawn, unsaved ignore areas
- applying an area to the whole build reports a failure instead of hanging
- keydown listeners on the canvas no longer pile up (Delete ran twice)

Lists and loading
- the run selection is cleared when another build opens; delete, download and
  clear-ignore-areas act only on selected runs that are shown
- the zip names images by test and tags and never overwrites one
- a late answer for a run, a build or another project's build list no longer
  replaces the current one; a deleted run/build link closes cleanly

Smaller
- a non-JSON error (proxy HTML page) still logs out on 401 and shows a message
- login after an expired session returns to the full link, query included
- a role the server rejects reverts in the user list
- no trailing space in VRT_PROJECT on the profile page
- a failed login right after registering shows the error

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e effect flush

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@pashidlos
pashidlos merged commit e3c280c into master Oct 24, 2026
3 checks passed
@pashidlos
pashidlos deleted the claude/frontend-review-improvements-54c2a1 branch October 24, 2026 13:23
pashidlos added a commit that referenced this pull request Oct 24, 2026
- handleResponse: a successful response that is not JSON goes through the
  one rejection path instead of a second Promise.reject
- applying an ignore area to the whole build is one flat promise chain
- zip entry numbering uses a while loop over the name it tests
pashidlos added a commit that referenced this pull request Oct 24, 2026
… list and switcher (#442)

* build(lint): run SonarCloud's rules locally with eslint-plugin-sonarjs

SonarCloud only looks at a PR once it is open, so its findings arrived
after review (#438 → #439). eslint-plugin-sonarjs 4.2.2 runs the same rules
in `npm run lint`, the pre-commit hook and CI.

The 21 findings in the current code:
- window.open for "Open an issue in GitHub" now passes noopener,noreferrer,
  so the opened page gets no handle on the app's tab (test first)
- three nested ternaries and two one-case switch reducers rewritten as
  plain ifs; a float constant compared with toBeCloseTo
- todo-tag is a warning: the TODOs are debt to see, not a failed build
- off for Playwright specs only: assertions-in-tests (specs assert through
  helpers and page objects) and void-use (`void fixture` asks for a
  fixture used only for its setup)

* feat(projects): favourites, recently opened and search in the project list and switcher

A long project list was one unsorted grid, and the switcher on the build page
a plain select. Now:

- a star on each card marks a favourite; favourites are kept per user by the
  API (GET /projects/favorites, PUT/DELETE /projects/:id/favorite) and listed
  first, under "Favourites"
- the last five projects opened are listed next, under "Recently opened",
  kept in this browser per user
- the rest follow by name; the order is the same in the list and switcher
- a search field filters by name, id or main branch, keeps the query in the
  URL (?q=) and takes focus on "/"
- the switcher on the build, variations and profile pages is an autocomplete
  with the same groups and search
- Builds and Variations are router links: they used to reload the whole app

An API without favourites (older backend) answers 404: the list then works
without them instead of greeting every login with an error. The star goes
back and the error shows if saving it fails.

ProjectCard, ProjectGrid and ProjectSearchField keep the page under 300 lines.
The e2e sign-in fixture answers no favourites by default; new specs cover
starring, the order, search, "/", the switcher and navigation without a
reload. screens.spec gains the grouped list, a search without matches and
the open switcher; baselines regenerated in Docker.

* test: raise the coverage floors to the reached level

* fix(projects): keep the switcher where the select was; address SonarCloud on #442; wait before legacy screenshots

- The project switcher had a margin above its label, which the select it
  replaced had not: the label, the name and everything under them sat 8px
  lower on the build, variations and profile pages. Only the sides and the
  bottom keep the inset now, and the layout matches master again.
- SonarCloud: the two favourite mocks drop an async they never awaited, and
  the lint config's comment no longer reads as an open task. require-await
  (Sonar's S7503, outside eslint-plugin-sonarjs) runs as a warning, since
  older tests and mocks have 40 such functions.
- The per-spec "renders" tests of six pages took their screenshot before the
  lazy route had rendered, so they flaked (on master too) and an update run
  wrote the empty page as the baseline. They wait for the page's heading and
  for the network to settle first.

Baselines regenerated in Docker; those within the 100px tolerance of master
are master's again.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant