expand the end-to-end suite to cover every authenticated route - #1477
Merged
Conversation
Adds Playwright coverage for the routes that had none (static pages, user profile, subject detail, upload, group management and all five admin screens), plus a server-side authorization block that drives the API directly to pin the access control the admin screens depend on. Supporting changes: - data-testid attributes in apps/web for the elements the new specs select - authenticateWithToken fixture, so a spec can act as a specific seeded user instead of a worker-cached role; authenticateAs now delegates to it - ApiClient.findGroupById, to check whether a refused write took effect Two defects were fixed directly rather than filed, because they blocked writing the tests at all: - DisclaimerProvider used data-test-id (hyphenated) on all four of its selectors, so the dialog was unreachable by Playwright's testid convention - UserDropup's trigger called setIsOpen on top of Radix's own controlled open/onOpenChange handling, so one real click fired both handlers and the stale-state toggle intermittently closed the menu again Everything else found along the way is described in BUGS.md rather than asserted as passing behavior. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every unfixed finding is now filed on the tracker (DouglasNeuroInformatics#1468-DouglasNeuroInformatics#1476, plus DouglasNeuroInformatics/libui#108 for the one that belongs to that package rather than this repo). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The file was a staging area for findings made while writing the suite. Each one is now an issue (DouglasNeuroInformatics#1468-DouglasNeuroInformatics#1476, plus DouglasNeuroInformatics/libui#108), so the spec comments that pointed at it cite the issue instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each one came down to the suite depending on a value only a developer's .env holds, since CI generates .env from .env.template. Three specs log in through the real form -- deliberately, so an init script cannot re-authenticate them -- and so never received the appState init script that only ran inside authenticateWithToken. They met the app's in-code first-run defaults, where the walkthrough overlay covers the sidebar and swallows every click. That stays invisible locally because a dev .env sets VITE_DEV_DISABLE_TUTORIAL=true where the template leaves it false. Seed appState from an autouse fixture instead, for every test whatever its auth path, leaving authenticateWithToken to inject only the token; the disclaimer helper this makes redundant is deleted. Read CONTACT_EMAIL through support/env.ts rather than asserting a literal that only matches one machine. Scope the graph assertion to its own series' dots: the dashed group-trend line plots points carrying the same class once the linear-model query resolves, so an unscoped locator matched one or two elements depending on timing. Retry an instrument option that detaches mid-click, and give that test the budget its UI seeding needs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
thomasbeaudry
added a commit
that referenced
this pull request
Jul 30, 2026
The test expected useState form values to persist across route navigation, but nothing in the component saves form state on unmount. This test was introduced by PR #1477 into split/mailer and has never passed in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
thomasbeaudry
added a commit
that referenced
this pull request
Jul 30, 2026
Reconciles the SMTP-only refactor with the two mail fixes pushed to the branch and with everything main gained since (#1441, #1444, #1477). The branch had patched the HTTP transport rather than removed it, so the conflicts were resolved toward the maintainer's decision 1: the HTTP client and SigV4 signer go, which makes both patches unnecessary rather than wrong. `isMailEnabled` no longer needs an HTTP branch, and the provider names come back out of `eslint.config.js` since no JSX mentions them any more. Kept from the other side: - #1441's `getDefaultAssignmentExpiry` replaces the hardcoded one-year default in the create-assignment form. - #1444's `$Email`/`$PhoneNumber`/`omittedIfBlank` validation helpers in the user-create form, replacing the old `PHONE_REGEX`. - The deduplicated welcome-email notification in `useCreateUserMutation`, now also opted out of the router error boundary so the call site's own password-error handling is the only path. - #1477's page objects and specs, unioned with the two added here. `SaveStatus.tsx` is restored from main: #1442 has landed and `admin/settings` now consumes it, so decision 4's "pick it back up once #1442 lands" applies. `activeLanguages` and `utils/languages.ts` are still absent from main, so those stay dropped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Expands the Playwright suite from the handful of flows it covered to every authenticated route in
apps/web, and adds a server-side authorization block that drives the API directly.New coverage
static-pages/aboutrelease info (web, API, gateway healthcheck),/contactvalidation and its mailto link, unauthenticated redirects for bothuser-profilesubject-detailuploadgroup-manageadmin-settingsadmin-brandingadmin-instrument-reposadmin-audit-logsaccessible-instrumentsauthorizationadmin-management,auth,dashboard,datahub,disclaimer,gateway-assignment,instrument-completion,remote-assignmentandstart-sessionall gained cases as well.Server-side authorization
_app/route.tsx'sbeforeLoadchecks that a token exists, not what role holds it, and no route under/adminadds a check of its own — so a non-admin who navigates there directly gets the real screen (#1470). What actually protects those screens is the API, soauthorization.spec.tsnow asserts that half directly rather than through the UI:POST/PATCH/DELETE /v1/users,PATCH /v1/setup,POST /v1/instrument-repos,GET /v1/audit/logs) returns403for bothGROUP_MANAGERandSTANDARD.@RouteAccesssees only the subject type there and the row scoping inGroupsServiceis the whole check.GET /v1/usersreturns only the acting user's own group for aGROUP_MANAGER, and only their own account for aSTANDARDuser. This is what makes a populated table on/admin/usersacceptable rather than a leak.POST /v1/groupsis deliberately absent from that table: a group manager is wrongly allowed it (#1468), and asserting the current behaviour would lock the defect in.Supporting changes
data-testidattributes inapps/webfor the elements the new specs select.authenticateWithTokenfixture, so a spec can act as a specific seeded user rather than a worker-cached role;authenticateAsnow delegates to it, which removes a hand-rolledaddInitScriptblock fromgroup-manage.ApiClient.findGroupById, used to check whether a refused write actually took effect.testing/AGENTS.mdfixture table updated.Two defects fixed inline
Both blocked writing the tests at all, so they were fixed here rather than filed:
DisclaimerProvideruseddata-test-id(hyphenated) on all four of its selectors, so the disclaimer dialog was unreachable by Playwright's default testid convention.UserDropup's trigger calledsetIsOpen(!isOpen)in anonClickon top of Radix's own controlledopen/onOpenChangehandling on the sameDropdownMenu.Trigger. Both fire on one real click, and the second — driven by staleisOpen— intermittently flipped the menu straight back shut, so a normal click on the sidebar user menu sometimes silently did nothing.Everything else found along the way
Filed rather than asserted, so no broken behaviour is pinned as correct: #1468, #1469, #1470, #1471, #1472, #1473, #1474, #1475, #1476, and DouglasNeuroInformatics/libui#108.
Test plan
pnpm lint— passes repo-wide (33/33).pnpm test:e2e— runs in CI on this PR. It was not run locally: a dev stack was holding the API port, and Playwright's own servers cannot start alongside it (running against the dev stack is not an option, sinceglobal/teardown.spec.tsdrops the database).🤖 Generated with Claude Code