dorfteich/apps/web/e2e/README.md
Claude Opus 4.8 406886c56c
Some checks failed
CD / Build and push images (push) Successful in 3m5s
CI / Lint, typecheck, test (push) Successful in 2m31s
CI / Auth e2e pack (push) Failing after 2m0s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 12s
Add label- and page-scope access rules UI including deny (#55)
Pond Admins configure the vision's fine-grained cases through a plain-language
surface, on top of the base roles from #54.

- shared: `AccessRuleView` (a grant enriched with subject/scope display names)
  and pure conflict helpers `scopeSpecificity`/`sameGrantSubject`/
  `isRuleShadowed` (unit-tested) for the client-side shadowed-rule hint. New
  `access` i18n namespace (de+en) with sentence templates (ADR 0012).
- api: `GET /ponds/:id/grants/access-rules` (Pond-Admin) returns the pond's
  grants enriched with each user's display name and each label/page scope's
  name, resolved in one batched query per kind.
- web `access/`: `AccessRulesManager` in Pond Settings — the pond's rules
  grouped by subject and rendered as readable de/en sentences ("Anna may not
  edit pages labeled “Confidential”"), an add form (subject = member or the
  `signed-in`/`public` pseudo-subjects; scope = label from the tree or a
  specific page; role; allow/deny) that warns when a rule would be shadowed by
  a more specific existing one (shared algorithm) and requires an explicit
  confirmation before granting anything to `public`. Semantics are the shared
  resolver's — the UI only reflects permissions.md.
- tests: shared `conflicts.test.ts`; an api db case for the enriched endpoint;
  a browser `access-rules` pack that configures BOTH vision patterns through
  the UI and verifies their effect end to end — "deny label X" (an editor
  loses a labelled page) and "only label Y" (a signed-in non-member, new
  `fixture-viewer`, reads only the labelled pages) — plus the shadow hint and
  the public confirmation, with its own CI step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-09 21:45:31 +02:00

92 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# End-to-end tests
Playwright suites, most local-only — three run in CI/CD:
| Suite | Target | Where it runs |
| ----------------- | --------------------------------- | --------------------------------------------------------------------------- |
| `smoke.spec.ts` | any deployed stage | CD pipeline against `https://test.dorfteich.cloud` after every deploy |
| `auth.spec.ts` | full local stack **with Mailpit** | CI job `auth-e2e` on every PR/push; locally against the dev stack |
| `content.spec.ts` | full local stack | same CI job `auth-e2e` (a second step), right after the auth pack |
| everything else | full local stack | locally only — `editor`/`sidebar`/`image`/`link`/`markdown`/`trash`.spec.ts |
`content.spec.ts` is the M2 content regression pack (issue #32): page
lifecycle, editor basics, image paste, Markdown round-trip, and trash —
enough to catch a regression across the whole content model without
re-running every edge case the feature-specific packs above already cover.
Its Markdown round-trip test is a real regression pin, not just a smoke
check: it compares the seeded "Every Element" fixture page's exported
Markdown byte-for-byte against the checked-in `content-page.md` (see
"Content fixtures" below) — any schema/serializer change that alters how a
node round-trips fails it, once the seed has re-run against the changed
code (build → migrate → seed → test, exactly CI's order).
## Running locally
```sh
# 1. Stack: database + Mailpit, api (3001), web dev server (5173)
docker compose -f deploy/compose/docker-compose.yml -f deploy/compose/compose.dev.yml up -d db mailpit
DATABASE_URL=postgresql://dorfteich:dorfteich@localhost:5434/dorfteich pnpm --filter @dorfteich/api db:seed
DATABASE_URL=postgresql://dorfteich:dorfteich@localhost:5434/dorfteich PORT=3001 pnpm --filter @dorfteich/api start:dev &
pnpm --filter @dorfteich/web dev &
# 2. Tests
E2E_BASE_URL=http://localhost:5173 E2E_MAILPIT_URL=http://localhost:8025 pnpm --filter @dorfteich/web e2e
```
`auth.spec.ts` skips itself when `E2E_MAILPIT_URL` is unset, so the CD
smoke run never trips over it.
## Fixture matrix
Seeded by `pnpm --filter @dorfteich/api db:seed` (idempotent — re-running
never duplicates). Shared password: `fixture passwort 123`. Fixtures exist
only on dev machines and disposable CI/Test databases.
| Username | State | Purpose |
| ----------------- | ------------------- | ---------------------------------------------------------------------------------------- |
| `fixture-admin` | active, Site Admin | admin UI/permissions cases |
| `fixture-user` | active | regular journeys, settings, sessions |
| `fixture-editor` | active | second regular account for the collab-permissions pack (reader/editor of another's pond) |
| `fixture-viewer` | active | signed-in non-member for `authenticated`/`public` access-rule cases (issue #55) |
| `fixture-pending` | e-mail not verified | unverified-login cases |
## Content fixtures
`db:seed` also creates a **shared** pond `content-fixtures` (owned by
`fixture-user`) with two pages, for the content regression pack and manual
QA:
- **Every Element** (`every-element`) — every editor schema node and mark
(issue #24: headings 14, all list types, table, blockquote, code block,
horizontal rule, hard break, and all five marks). Loaded from the
checked-in `apps/api/prisma/fixtures/content-page.yjs`, a Yjs snapshot
generated from the human-readable `content-page.md` next to it —
`content-page.md` is the thing to read or edit; the `.yjs` file is a
build artifact of it, not source.
- **Fixture Image** (`fixture-image`) — one real, servable uploaded image
(the placeholder `fileId` inside the Markdown fixture above is not a
real attachment; this page's image is).
Regenerating after editing `content-page.md`:
```sh
pnpm --filter @dorfteich/api fixtures:regenerate
```
This is deterministic — re-running without editing the Markdown produces a
byte-identical `.yjs` file (the script pins the Yjs document's `clientID`,
which is otherwise randomized per `Y.Doc` instance) — and it refuses to
write a snapshot that isn't a fixed point of the Markdown round-trip
(`docToMarkdown(markdownToDoc(x)) === x`), so a stale fixture can't get
checked in silently.
## Conventions
- New feature packs get their own `<feature>.spec.ts` next to these and
extend the fixture matrix here (permission matrix arrives with M5,
issue #60).
- Use `contextForUser()` from `helpers.ts` for signed-in tests — it logs
in through the api and hands you a browser context with the session
cookie, no UI login repetition.
- Flaky tests are defects (ADR 0014): fix or quarantine immediately.