Skip to content

test(e2e): cover SSO login, user deletion, the build menu and login errors; fix the build menu dialogs - #435

Merged
pashidlos merged 4 commits into
masterfrom
claude/e2e-oidc-login
Oct 24, 2026
Merged

pashidlos merged 4 commits into
masterfrom
claude/e2e-oidc-login

Conversation

@pashidlos

@pashidlos pashidlos commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Adds 32 Playwright tests (6 of them screenshot tests) for the e2e gaps left after #423, and fixes a bug they found in the build menu. Follows the existing pattern: page objects in integration_tests/pages, every API call mocked in utils/mocks.ts.

SSO login (oidc.spec.ts, 11 tests; #412 had no e2e)

  • SSO off: an API that can't answer /users/oidc/enabled (404) gets the password form.
  • SSO on:
    • The login page offers the SSO button instead of the password form, and clicking it goes to the provider.
    • A callback with #token= calls /users/me with that token as the Bearer, stores the user with the token, and lands on /projects with the fragment gone.
    • #error= shows "SSO login failed." A token the API rejects shows its message and stores nothing.
    • /register goes to /login.
    • An SSO user's profile has the email locked and no password form.
  • Auto redirect, set through a mocked env-config.js (the real file plus an override):
    • /login goes straight to the provider.
    • No bounce back while handling the callback.
    • No redirect after an explicit logout, and the button still works afterwards.

User list (userList.spec.ts, 3 tests)

  • Deleting the selected users sends their ids and removes their rows.
  • The delete button is disabled until a row is selected.
  • Selecting yourself shows "You cannot delete yourself." and sends nothing.

Build menu (buildMenu.spec.ts, 8 tests)

  • Stop a running build (PATCH { isRunning: false }); a finished build has no Stop.
  • Edit the CI build id (PATCH { ciBuildId }), with too short an id not sent.
  • Delete a build. Deleting the selected build drops buildId from the URL.
  • After a failed edit or delete, the next menu opens as a menu (see the fix below).

Login (login.spec.ts, 4 tests)

  • A refused login shows the API's message.
  • An invalid email is not sent.
  • After a redirect to login, the user is sent back to the page they asked for.
  • A plain login lands on /projects.

Fix: build menu dialogs

The edit and delete dialogs for a single build only closed on success. After a failed save the open flag stayed set. The next time any build's menu was opened, the old dialog came straight back, now titled for that build and still holding the previous input, so clicking Edit would set that build's CI build id to the wrong value. Delete had the same flaw. Both dialogs now close on submit, together with the menu, and the edit input is cleared. Both "opens nothing by itself" tests fail without the fix.

Screenshot tests

Six new baselines, rendered in Docker with npm run test:pw:docker:update -- missing …. missing writes only the absent ones, so no existing baseline changed.

  • login-page-sso.png: the login page with SSO on.
  • profile-page-sso.png: an SSO user's profile, with the email locked and no password card.
  • build-menu.png: the build menu open on a running build, with Stop, Edit CI Build and Delete.
  • build-edit-dialog.png: the CI build id dialog. The build menu stays open behind it; that is existing behaviour, not changed here.
  • project-page-builds-error.png: the "Builds could not be loaded" state from test(e2e): cover roles, session and API errors, and the thin specs; fix stuck loading on fetch errors #423.
  • user-list-selection.png: the user list with two users selected and delete enabled.

SonarCloud

The 8 new issues are fixed. The new mock helpers drop async, since they only return page.route's promise (S7503), and the build item regex uses String.raw (S7780).

Test run

  • Docker (npm run test:pw:docker -- --retries=0 --workers=2): 162/162 passed in 2.1 min, screenshot tests included.
  • A run with the default 6 workers had timeouts across specs, including ones this PR doesn't touch. The host's load average was about 40 on 12 cores at the time, because other sessions were running tests.
  • Lint is clean. tsc: 25 errors before and after, none in the touched files.

Merging with #434

#434 also changes utils/mocks.ts and pages/components/BuildList.ts. Here both changes only add code: new helpers at the end of mocks.ts, and new members in BuildList. Whichever PR merges second can keep both sides. The new spec files don't overlap with #434's.

🤖 Generated with Claude Code

Pavlo Strunkin and others added 4 commits October 24, 2026 12:28
…rrors; fix the build menu dialogs

- oidc.spec: SSO button instead of the password form, the redirect to the
  provider, signing in with the token from the callback, provider and API
  refusals, /register sent to /login, auto redirect (incl. no bounce while
  handling the callback and none after logout), the SSO profile, and the
  password form when the API cannot say
- userList: delete selected users, delete off until a selection, cannot
  delete yourself
- buildMenu.spec: stop a running build, no stop for a finished one, edit
  the CI build id (incl. validation), delete a build and drop it from the
  url
- login: refused login, invalid email not sent, return to the page that
  asked for the login, plain login lands on the projects
- mocks: env-config.js overrides, OIDC flag and provider, /users/me,
  login, user deletion, build patch

Fix: the build menu's edit and delete dialogs closed only on success.
After a failure the open flag stayed set, and the next build's menu
opened straight into the old dialog, prefilled with the old input, so
"Edit" would change the wrong build's CI build id. Both now close on
submit, with the menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Screenshot tests, baselines rendered in Docker with
`npm run test:pw:docker:update -- missing ...`:
- oidc: the SSO login page, the profile of an SSO user
- buildMenu: the build menu, the CI build id dialog
- errors: the project page whose builds did not load
- userList: the list with users selected

SonarCloud: the new mock helpers drop `async`, since they only return
page.route's promise (S7503), and the build item regex uses String.raw
(S7780).

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

Copy link
Copy Markdown

@pashidlos
pashidlos merged commit 2be2801 into master Oct 24, 2026
3 checks passed
@pashidlos
pashidlos deleted the claude/e2e-oidc-login branch October 24, 2026 09:48
pashidlos pushed a commit that referenced this pull request Oct 24, 2026
- the build menu's MoreVert icon keeps its data-testid explicitly, as the
  production bundle drops it under MUI 7 (see AGENTS.md)
- the six screenshots #435 added are regenerated in Docker for the new
  theme and reviewed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pashidlos added a commit that referenced this pull request Oct 24, 2026
…k mode (#434)

* chore: green baseline (typecheck, lint, husky 9, CI gates)

- tsc: type-only fixes for the 25 errors in src and the e2e project;
  typecheck now covers integration_tests and runs in CI
- eslint: root config, so a nested checkout does not pick up the parent's
- husky 9 with a real pre-commit hook (the husky v4 block did nothing)
- CI on Node 22 (.nvmrc), actions v4, lint/typecheck/test/build gates
- test-docker.sh keeps the Linux node_modules in a volume instead of
  overwriting the host's platform binaries

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

* docs: modernization plan, revised after two reviews

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

* test: move unit tests from Jest to Vitest

- Vitest 5 on the Vite config: the 47 tests run in ~1s instead of ~8s
- Testing Library 16 and user-event 14; renderWithProviders wraps a
  component in the app's providers for component tests
- the theme moves to src/theme so tests and App share it
- Node 22 in the Dockerfile and @types/node 22 (Vitest 5 needs them)

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

* docs: rules for contributors and coding agents

AGENTS.md describes the layout, the one-way import flow, conventions, how
tests are organised and which ids/labels/class names page objects depend
on. CLAUDE.md points at it; .claude/settings.json allows the project's
check commands and formats edited files. CONTRIBUTING.md and a PR template
carry the same definition of done.

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

* fix(types): Build.updatedAt is optional, as the UI already treats it

Giving the fixtures an updatedAt to satisfy the type added a 'Last run'
line to the screenshots; the components render it only when present.

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

* refactor: replace @mui/styles with sx and styled

@mui/styles is deprecated and does not work with MUI 7. Same CSS values;
the screenshot baselines match unchanged in Docker.

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

* test: cover services, helpers and context reducers

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

* build: ESLint 9 flat config that enforces the project layout

- ESLint 9 + typescript-eslint 8, react, react-hooks 5.2 (7.x adds React
  Compiler rules the current code does not meet), jsx-a11y, prettier
- no-restricted-imports keeps services/types/_helpers/constants out of
  the UI layers and bans @mui/styles and MUI internals; fetch only in
  src/services
- lint-staged runs eslint --fix before prettier
- tsconfig: bundler module resolution, automatic JSX runtime;
  Router.jsx becomes Router.tsx

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

* perf: load pages on first visit

The single 1.6 MB chunk becomes per-route chunks; the login screen no
longer waits for Konva, the data grid and the review UI.

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

* test: component tests for lists, dialog controls and socket updates

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

* test(e2e): project form engines, ignore areas, comments, hotkeys, matching variations

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

* fix: the full-size variation view no longer blanks the page

MatchingVariationsDialog took three icons as default imports from the
per-icon files; the production bundle hands those over as module objects
and React failed with error #130, unmounting the app. Named imports, as
everywhere else. The e2e test that pinned the crash now checks the view.

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

* test: component tests for forms and debounced inputs

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

* test: harden two new e2e tests; tests survive a loaded machine

- the approved-run hotkey test repeats its round until the dialog's
  hotkeys are bound; the full-size test no longer trips strict mode
- Testing Library waits up to 5s and Vitest 15s per test: under load the
  default 1s findBy timeout failed MUI dialogs at random

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

* build(deps): MUI X Data Grid 6 to 8

- selection is { type, ids } now: the table converts it both ways, and
  the header box's exclude list resolves against the rows on show
- valueGetter takes (value, row); custom toolbars need showToolbar;
  custom cells ask for display: flex to stay vertically centred
- the user list reads the selection with useGridSelector
- Vitest transforms the grid's ESM build (it imports a directory)
- two baselines regenerated in Docker: sub-pixel cell and sort-icon
  differences, nothing moved

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

* refactor: DebouncedTextField replaces react-debounce-input

Same behaviour as the library at the three call sites, pinned by the
component tests: reports after 300 ms, ignores input shorter than
minLength while typing but reports a deletion below it as empty, Enter
and blur report a pending change at once, an outside value (reset) is
shown unless a change is pending, and a pending change survives unmount.

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

* build(deps): MUI 7 and react-material-ui-form-validator 5

- Grid becomes GridLegacy, which lays out exactly as MUI 5's Grid did;
  pages move to the new Grid/Stack as the redesign rewrites them
- icons only carry their data-testid in development builds now, and e2e
  runs on the production bundle: the icons page objects find keep it
  explicitly
- Switch drops the deprecated inputProps in favour of slotProps.input, so
  aria-labels go there; switches have role switch, pagination marks the
  current page with aria-current=page (page objects follow)
- six baselines regenerated in Docker: chip label text moves by a pixel

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

* test: enforce the coverage reached (51% lines, from 6.5%)

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

* feat(ui): new MUI theme with dark mode and refreshed screens

- design tokens in src/theme: the VRT logo's cyan as primary, slate
  neutrals, Inter (bundled), 10px radius, quiet component overrides
- light and dark colour schemes on CSS variables; the header toggle
  follows the system until the viewer picks, and remembers the pick
  (localStorage vrt-color-mode)
- one meaning per status colour everywhere: new info, unresolved warning,
  failed error, passed/approved success; a theme test keeps their text at
  WCAG AA contrast in both modes
- header: product name, colour mode, help and avatar menu with the
  signed-in user; login and register share a centred AuthLayout; the
  project list is a grid of cards with an 'Add project' button
- the layout is a flex column instead of a 10%/90% height split
- hard-coded colours in the review dialog, the tour and the variations
  dialog now come from the theme
- e2e: colour mode toggle, persistence, system preference and three dark
  screenshots; baselines regenerated in Docker and reviewed one by one;
  the wrap test gets a name long enough to wrap in any font

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

* docs(AGENTS): Grid, slotProps and icon test id rules learned in the MUI 7 move

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

* fix: address the branch review

- reload once when a lazy page chunk is gone after a redeploy, instead of
  leaving a blank page
- the screenshot canvas stays white in dark mode, so transparent pixels
  look the same to every reviewer
- the table's selection model is memoised, so the grid does not re-sync
  on every render

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

* ci: cancel superseded PR runs, cache npm for the Docker e2e run; fix test matcher types

- both workflows cancel the previous run of a pull request on a new push;
  runs on master always finish
- test-docker.sh mounts npm's cache into the container (a host folder in
  CI, kept by actions/cache keyed on package-lock.json; a named volume
  locally) and installs with --prefer-offline
- jest-dom 7 and its vitest entry in tsconfig: 6.1 augmented
  @vitest/expect, which Vitest 5 no longer reads, so CI's typecheck saw
  no DOM matchers (the local check borrowed types from a parent folder)

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

* ci: name the Docker volume with SHA-256 (SonarCloud S4790)

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

* fix: address the code review of #434

- the chunk-reload guard allows one reload a minute and survives blocked
  storage, instead of being cleared on every load (could loop)
- the avatar menu's user block is a ListSubheader, so opening the menu
  focuses its first item rather than the name
- ColorModeToggle moves to components (the theme layer no longer imports
  UI) and holds its place until the colour mode is known
- the white canvas and thumbnail backgrounds are named tokens
  (imageSurface), per AGENTS.md
- AppBar and DataGrid overrides work under a theme without CSS variables
- page object nits from SonarCloud (.at(-1), String.raw)
- the superpowers plan leaves the repo: it was a working document for this
  PR; what lasts is in AGENTS.md and the PR description

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

* test(e2e): a screenshot of every view, in light and dark

screens.spec.ts renders 25 views and states in both colour schemes (50
baselines, generated in Docker, stable over three repeats): sign-in
(password and SSO), registration, the project list (full and empty)
with its create/edit/delete dialogs, the project page as table, grid,
grouped, with a selection and without builds, the review dialog with an
ignore area, comments and the matching variations dialog, the variation
list and details, profile, users, both header menus, the guided tour and
an error toast. AGENTS.md makes it the place new views are added.

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

* feat(ui): second redesign pass — variations, profile, users, empty states, tour

- PageHeader and EmptyState components shared by the pages
- variations: page title with the project select, merge and filters as
  panels, cards with a framed thumbnail, tag chips and the branch;
  details: the name over its branch and axes, baseline history as cards
- profile and users get the page header, theme buttons and spacing; the
  API key reads as code
- empty states for the build list, the project page without a build,
  an empty project list and an empty variation list
- the guided tour takes the theme: radius, type, a filled next button
- screens.spec baselines regenerated in Docker and reviewed

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

* test(e2e): fit #435's new tests to MUI 7 and the redesign

- the build menu's MoreVert icon keeps its data-testid explicitly, as the
  production bundle drops it under MUI 7 (see AGENTS.md)
- the six screenshots #435 added are regenerated in Docker for the new
  theme and reviewed

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

* fix: clear the SonarCloud issues on #434

- BuildList: the loading/failed/empty states move to BuildListPlaceholder
  (cognitive complexity 19 → under 15)
- screens.spec names its extended fixture test, so Sonar sees the tests
  (S2187 'add some tests to this file' was a false positive)
- theme.ts imports the Data Grid theme augmentation as a side effect
  instead of an empty type import
- mocks and the fetch stub without await are plain functions returning
  their promise; canvas widths are read in parallel

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

* feat(ui): rework the dark theme on neutral graphite

- surfaces in three steps instead of navy: page #111318, panels #181b21,
  and menus, dialogs and popovers one step lighter (#20242c) with a hairline
  border where light mode uses a shadow
- the logo cyan toned down for dark (#5ec3d6), softer status hues; filled
  status chips become a tint with coloured text; selected rows a light
  tint; tooltips lifted off the page
- contrast stays at WCAG AA (theme test); dark baselines regenerated with
  --update-snapshots=all, since the 0.2 pixel threshold reads navy and
  graphite as the same colour

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

---------

Co-authored-by: Pavlo Strunkin <strunkin@Pavlos-MacBook-Pro.local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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