Bump @levkin/playkit to v0.3.1 and land the network-interception work it unblocked: startNetworkErrorMonitor on gallery/search/upload/manage-users, plus real interceptNetworkCall spies replacing brittle waitForResponse/text-scraping on upload and the search-filter UI.
Favorites + People filter UI e2e (storageState-based).
Widen Zod schemas: FastAPI login TokenResponse + /auth/meUserResponse, and the Prisma-backed gallery SearchResponse.
New PROD smoke spec (prod.smoke.spec.ts) — opt-in via PROD_BASE_URL; verified against a real public host, stays a no-op skip until the PROD LXC exists.
New NextAuth (browser-session) write-gate spec (viewer.write-gates.spec.ts) — provisioned a third, independent auth-DB viewer user (hasWriteAccess=false) via ansible's provision-punimtag-e2e-user.py; viewer 403s vs admin passes-through on POST /api/faces/{id}/identify.
admin-frontend Vitest coverage + CI job (previously had no test infra).
ROADMAP.md updated; documented that DEV (LXC 9101) is ~19 commits behind dev HEAD, which blocks two other uncommitted specs left as-is (not part of this PR).
Test plan
Full local e2e run against DEV (npx playwright test --project=setup --project=chromium): 35 passed, 6 skipped (mail specs need Mailpit env)
tsc --noEmit clean across e2e/
CI green on this PR (Gitea Actions)
## Summary
- Bump `@levkin/playkit` to `v0.3.1` and land the network-interception work it unblocked: `startNetworkErrorMonitor` on gallery/search/upload/manage-users, plus real `interceptNetworkCall` spies replacing brittle `waitForResponse`/text-scraping on upload and the search-filter UI.
- Favorites + People filter UI e2e (storageState-based).
- Widen Zod schemas: FastAPI login `TokenResponse` + `/auth/me` `UserResponse`, and the Prisma-backed gallery `SearchResponse`.
- New PROD smoke spec (`prod.smoke.spec.ts`) — opt-in via `PROD_BASE_URL`; verified against a real public host, stays a no-op skip until the PROD LXC exists.
- New NextAuth (browser-session) write-gate spec (`viewer.write-gates.spec.ts`) — provisioned a third, independent auth-DB viewer user (`hasWriteAccess=false`) via ansible's `provision-punimtag-e2e-user.py`; viewer 403s vs admin passes-through on `POST /api/faces/{id}/identify`.
- `admin-frontend` Vitest coverage + CI job (previously had no test infra).
- Fixed lingering `res.data: unknown` / Mailpit-vs-Mailtrap TS errors in mail/catalog specs.
- ROADMAP.md updated; documented that DEV (LXC 9101) is ~19 commits behind `dev` HEAD, which blocks two other uncommitted specs left as-is (not part of this PR).
## Test plan
- [x] Full local e2e run against DEV (`npx playwright test --project=setup --project=chromium`): 35 passed, 6 skipped (mail specs need Mailpit env)
- [x] `tsc --noEmit` clean across `e2e/`
- [ ] CI green on this PR (Gitea Actions)
Bump @levkin/playkit to v0.3.1 (network interception helpers) and land the
work it unblocked: startNetworkErrorMonitor on gallery/search/upload/manage-
users, Favorites + People filter UI e2e (storageState), and a fix for the
lingering `res.data: unknown` / Mailpit-vs-Mailtrap type errors in the mail
and catalog specs. Also adds Vitest coverage + a CI job for admin-frontend,
which had no test infra before this.
- upload.smoke.spec.ts / gallery.search-filters.spec.ts: replace
waitForResponse/text-scrape with typed interceptNetworkCall spies (status
+ JSON shape) on the real upload POST and the filtered /api/search GET.
- api.fastapi-login.spec.ts: Zod-validate the FastAPI TokenResponse and
/auth/me UserResponse instead of loose casts. gallery.search-filters.spec.ts
gets the same treatment for the Prisma-backed SearchResponse.
- prod.smoke.spec.ts: opt-in (PROD_BASE_URL) health + login-page check on a
real PROD host; skips as a no-op until the PROD LXC exists (none does yet
per `pct list` — see ROADMAP).
- viewer.write-gates.spec.ts: NextAuth (browser-session) hasWriteAccess gate
on POST /api/faces/{id}/identify — viewer 403s, admin passes through.
Provisioned a third, independent auth-DB user (e2e-viewer@levkine.ca,
hasWriteAccess=false) for this via ansible's provision-punimtag-e2e-user.py
(see that repo for the vault/Infisical/Gitea/Vaultwarden side).
admin.manage-users.spec.ts only opened/closed the panel; the actual
create/edit/delete flow through the UI was untested. Adds a page object
(ManageUsersPanel) and two specs: full CRUD through the panel, and a
causal cross-session check that deactivating a user via the admin UI
immediately revokes that user's already-open session (NextAuth jwt
callback re-checks isActive on every request, per issue #57's fix).
Every run creates and tears down its own disposable e2e-manage-test-*
account since punimtagdev shares its Postgres DB with MirrorMatch.
CI's first run showed the Edit button's aria-label exact-match locator
timing out against the live table even though the same row resolved
fine for badge-text reads moments earlier — switch to positional
lookup (Edit is always the first action button, Deactivate/Delete the
last) instead of relying on exact accessible-name matching.
statusBadgeText(email) after deactivating a user hung for the full test
timeout: ManageUsersContent.handleEditUser() refetches the list with the
current statusFilter ('active' by default) after every save, so the
just-deactivated row silently drops out of view — the locator then waits
forever for a row that filter will never show again (no actionTimeout is
configured, so only the outer test timeout eventually kills it).
Add ManageUsersPanel.showAllUsers() and switch to it before reading the
badge. Also gave the test more headroom (60s -> 120s) since it drives two
full UI logins plus create/edit/verify against a shared-CI DEV LXC.
Verified: full local suite green (37 passed, 6 skipped) against DEV.
ilia
merged commit 96351288f0 into dev2026-07-14 22:24:07 -05:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
@levkin/playkittov0.3.1and land the network-interception work it unblocked:startNetworkErrorMonitoron gallery/search/upload/manage-users, plus realinterceptNetworkCallspies replacing brittlewaitForResponse/text-scraping on upload and the search-filter UI.TokenResponse+/auth/meUserResponse, and the Prisma-backed gallerySearchResponse.prod.smoke.spec.ts) — opt-in viaPROD_BASE_URL; verified against a real public host, stays a no-op skip until the PROD LXC exists.viewer.write-gates.spec.ts) — provisioned a third, independent auth-DB viewer user (hasWriteAccess=false) via ansible'sprovision-punimtag-e2e-user.py; viewer 403s vs admin passes-through onPOST /api/faces/{id}/identify.admin-frontendVitest coverage + CI job (previously had no test infra).res.data: unknown/ Mailpit-vs-Mailtrap TS errors in mail/catalog specs.devHEAD, which blocks two other uncommitted specs left as-is (not part of this PR).Test plan
npx playwright test --project=setup --project=chromium): 35 passed, 6 skipped (mail specs need Mailpit env)tsc --noEmitclean acrosse2e/- upload.smoke.spec.ts / gallery.search-filters.spec.ts: replace waitForResponse/text-scrape with typed interceptNetworkCall spies (status + JSON shape) on the real upload POST and the filtered /api/search GET. - api.fastapi-login.spec.ts: Zod-validate the FastAPI TokenResponse and /auth/me UserResponse instead of loose casts. gallery.search-filters.spec.ts gets the same treatment for the Prisma-backed SearchResponse. - prod.smoke.spec.ts: opt-in (PROD_BASE_URL) health + login-page check on a real PROD host; skips as a no-op until the PROD LXC exists (none does yet per `pct list` — see ROADMAP). - viewer.write-gates.spec.ts: NextAuth (browser-session) hasWriteAccess gate on POST /api/faces/{id}/identify — viewer 403s, admin passes through. Provisioned a third, independent auth-DB user (e2e-viewer@levkine.ca, hasWriteAccess=false) for this via ansible's provision-punimtag-e2e-user.py (see that repo for the vault/Infisical/Gitea/Vaultwarden side).statusBadgeText(email) after deactivating a user hung for the full test timeout: ManageUsersContent.handleEditUser() refetches the list with the current statusFilter ('active' by default) after every save, so the just-deactivated row silently drops out of view — the locator then waits forever for a row that filter will never show again (no actionTimeout is configured, so only the outer test timeout eventually kills it). Add ManageUsersPanel.showAllUsers() and switch to it before reading the badge. Also gave the test more headroom (60s -> 120s) since it drives two full UI logins plus create/edit/verify against a shared-CI DEV LXC. Verified: full local suite green (37 passed, 6 skipped) against DEV.