Three admin/index pages (groups/tags/users) — each renders a single
"Wähle X aus der Liste" prompt for the desktop split-view layout.
AuthHeader: brand link href + wordmark.
PersonsEmptyState: empty heading + explanation text.
6 tests across five small files.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tag link href, document-count visibility branch, color-dot at depth 0
vs deeper, aria-current matrix, children list rendering, collapse-map
hides children, expand/collapse toggle for nodes with children.
9 tests covering ~30 branches in the recursive tree-node component.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backfill cards rendered, both backfill buttons enabled by default,
no success banner before any action. Smoke-level coverage of the
admin maintenance page.
5 tests covering basic render branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Year divider rendering, distinct-year branch, no-duplicate consecutive
years, no-divider for documents without documentDate, canWrite-gated
new-document link with senderId-only and senderId+receiverId href
variants.
7 tests covering ~20 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All-sections render when full permissions, users/invites hidden when
!canManageUsers, groups hidden when !canManagePermissions, tags hidden
when !canManageTags, system/ocr hidden when !canRunMaintenance,
flyout closed by default.
6 tests covering ~30 branches in the permission matrix.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hero state when no senderId set, results card when senderId set,
SinglePersonHintBar gating on senderId × !receiverId, empty-results
message branch.
5 tests covering ~15 branches in the orchestrator.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
register page (350 lines): hero render when no codeError, NO_INVITE_CODE
vs other-codeError card branches, form hidden when codeError set,
back-to-login link, form section rendering, prefill hydration of
firstName/lastName/email, prefill-hint visibility branch, hidden
code input with code-null fallback.
admin/users/new: heading, three card sections, group checkboxes
rendered, form-error banner branch, cancel link, submit button.
17 tests across two pages.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brand link, four primary nav links, admin link gated on isAdmin,
hamburger menu open/close state via aria-expanded. Mocks $app/state
so the page URL drives the active-route highlighting.
6 tests, ~30 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Title, all four section headings, secure Wikipedia link rel
attributes, five rule cards rendered, four klaerung chips rendered.
7 tests covering the static help page.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Heading with email, three card sections (profile/groups/password),
success vs error form banners, group preselection from editUser.groups,
cancel link, delete button. Mocks the confirm service.
7 tests, ~25 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
UserPasswordSection: input rendering, type=password attribute,
required-prop propagation in both directions.
CorrespondenzFilterControls: dual date label rendering, both DateInput
ids, value hydration from fromDate/toDate, change-event smoke check.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Four tests: discard link href, save button label, form attribute
wiring, formaction. Small focused component.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
enrich/done: heading, body, both CTA links.
documents/bulk-edit: empty-store onMount redirect to /documents,
loading spinner during in-flight fetch, error banner on backend error
code, error banner on fetch rejection. Mocks fetch via vi.spyOn so the
async branches are exercised without a real backend.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
admin/groups/new: heading, both permission group renderings (4 standard
+ 4 administrative checkboxes), form-error banner branch, cancel link
href, submit button form-attribute wiring, name input requiredness.
Mocks $app/navigation so beforeNavigate doesn't crash the test runner.
enrich/+: heading, empty placeholder vs populated count + start CTA,
start CTA href derived from documents[0].id, per-row title rendering,
bulk-select checkbox gated on canWrite.
16 tests across two files.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
users/[id]: full-name derivation across all four branches
(both/firstName-only/lastName-only/email fallback), avatar initials
matrix, email/contact row visibility tied to data presence.
admin/ocr/global: heading + back link, runs prop pass-through,
defensive default for missing history fields.
geschichten/[id]: title rendering, author full-name vs email fallback
vs null, publishedAt suffix conditional, persons and documents sections
gated on array length, edit/delete actions gated on canBlogWrite. Mocks
the confirm service since it requires a ConfirmDialog mounted in layout.
26 tests across three files.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PersonEditForm: PERSON vs INSTITUTION/GROUP visibility matrix (firstName,
title, alias, birth/deathYear toggle), lastName label switch, prop
hydration of all populated fields, fallback to PERSON for unknown type,
empty-string handling for null fields. 10 tests, ~30 branches.
SegmentationTrainingCard: trainingInfo null vs populated, block count
display, button disabled-state matrix (training × tooFewBlocks ×
serviceDown), too-few-blocks and service-down hints, success message
after a mocked fetch, training history heading. 10 tests, ~25 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each status (active / exhausted / revoked / expired) maps to a distinct
visual treatment via statusColor() — one focused test per branch
asserts the correct background class on a tbody element so the test
verifies user-observable behaviour rather than the internal switch.
Also covers: empty placeholder, loadError banner, filter chip
selection state, new-invite form toggle on button click, createError
message visibility inside the open form, created-invite success card
with shareable URL, revoke button gating to active invites only,
unlimited-uses display, no-expiry display.
16 tests, ~50 branches covered.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
empty state vs. populated, zoom controls visibility tied to node count,
URL ?focus= preselection (matching id selects, missing id does not),
zoom-out clamping safety. $app/state mocked at module boundary so the
test can drive page.url and page.data.canWrite without a SvelteKit
runtime.
Six tests focused on user-observable behaviour — one logical behaviour
per test (Sara's guidance).
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DocumentViewer: loading / error / no-scan / image rendering branches.
filePath conditionally drives the direct-download link in the error
state; fileUrl + non-PDF contentType drives the <img> render.
PersonalInfoForm: default render, prop hydration including the German
date conversion path, success/error banner branches, form action wiring.
profile/+page: notification-checkbox enabled/disabled depending on
hasEmail, no-email hint visibility, prefsSuccess/prefsError banners,
fallback when notificationPrefs is null.
20 tests across three files.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PersonDocumentList: empty/populated, year-range derivation across
no-date/single-year/multi-year inputs, sort toggle visibility (>1 doc),
sort-direction round trip, preview-limit + show-more expansion,
title→originalFilename fallback, no-date and no-location branches.
persons/new: PERSON vs INSTITUTION/GROUP visibility matrix
(firstName/alias/life-year fields toggle), lastName label switching
between Vorname/Nachname/Name, form-error banner, prior-form hydration,
cancel link href, fallback to PERSON for unknown personType.
24 tests across two files, hitting the 32+28 = 60 branches at the top
of the issue's leverage list.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CorrespondentSuggestionsDropdown: empty list still renders the static
heading and 'Alle Korrespondenten' row, populated rows when not loading,
loading hides correspondent rows, initials fallback (lastName-only when
firstName is null), click + keyboard selection, Escape closes.
PersonCard: full matrix of conditional UI — title visibility for PERSON
vs non-PERSON, avatar initials path (firstName+lastName vs lastName-only
fallback), PersonTypeBadge presence for non-PERSON types, alias, life
dates, notes, and the canWrite=true/false branches that gate the edit
link (Nora's authorization-rendering rule).
21 tests covering ~50 branches.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
+error.svelte: vi.mock('$app/state') drives the page state so each test
can assert one of the three rendering branches — populated error message,
distinct status code, and the 'Internal Error' fallback when page.error
is null.
forgot-password/+page.svelte: prop-driven tests for the four states —
default form, success banner, error message inside the form, and the
back-to-login link href.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PasswordChangeForm: tests the null/success/error/mismatch banner branches
plus the form action wiring.
FileSectionNew: tests the no-file/file-selected toggle, onfileParsed
callback invocation with the parsed metadata, the early-return when no
file is in the change event, and the suggestedTitle fallback path.
Eleven tests across two files. Both follow the UploadZone template (props,
File API synthetic input, vi.fn() callback spies).
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes scaffolding pages from initial Paraglide setup that were never
navigated to in production. Shrinks the measured coverage surface and
removes dead code from the production bundle. CLAUDE.md route tables
updated to drop the demo/ entry.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- frontend/login: derive cookie `secure` flag from request URL protocol.
Pre-PR the cookie was only read by SSR so the flag didn't matter; now
the cookie IS the API credential and must be Secure on HTTPS or it
leaks a 24h Basic token on plaintext networks. Dev runs over HTTP and
would silently lose the cookie if we hardcoded `secure: true`, so the
flag follows `event.url.protocol === 'https:'`.
- SecurityConfig: rewrite the CSRF-disabled comment. The old
"browsers block cross-origin custom headers" justification no longer
holds once /api/* is authenticated via the cookie. Make the
load-bearing dependencies explicit: SameSite=strict on the auth_token
cookie + Spring's default CORS rejection.
- AuthTokenCookieFilter:
- Scope to /api/* only. /actuator/health and similar must not be
cookie-authenticated.
- Refuse malformed percent-encoding (URLDecoder throws); forward the
request without a promoted Authorization rather than crash.
- Use isBlank() instead of isEmpty() per Nora.
- Javadoc warning: getHeaderNames/getHeaders exposes the Basic
credential; any future header-iterating logger must scrub
Authorization before logging.
- Tests: add `passes_through_unchanged_when_request_is_outside_api_scope`
(/actuator/health with cookie should NOT be wrapped) and
`passes_through_unchanged_when_cookie_value_is_malformed_percent_encoding`.
Tighten the explicit-header test to verify same-instance forwarding
rather than just header equality.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Extract isPureTextRelevance() private static method to replace the
7-clause inline boolean in searchDocuments
- Guard long→int cast in relevanceSortedPageFromSql to prevent silent
overflow at page ≥43M (CWE-190)
- resolvePersonName now uses the typed API client (createApiClient)
instead of raw fetch, aligning with project conventions
- Update DocumentServiceTest stubs to match new FTS path (findFtsPageRaw
+ findAllById instead of findAllMatchingIdsByFts)
- Rewrite page.server.spec.ts person-name tests to mock via path-based
API dispatch, matching the new api.GET call site
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three root causes prevented filters from reflecting the URL after SvelteKit
client-side navigation:
1. +page.server.ts now resolves sender/receiver display names in parallel with
the document search (UUID validation + silent 404 drop), so initialSenderName
/ initialReceiverName land in server data ready for the UI to use.
2. +page.svelte passes initialSenderName, initialReceiverName, and navKey
(incremented via untrack on every navigation) down to SearchFilterBar.
The untrack() prevents the effect from re-running due to its own navKey write.
3. SearchFilterBar forwards navKey as resetKey to each PersonTypeahead, which
already had a void resetKey guard added in the previous commit.
Together these ensure that after navigating to /documents?senderId=<uuid> the
typeahead shows the person's display name, and clicking × reset clears it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The SvelteKit prerender crawler cannot reach this route because
hooks.server.ts redirects all non-public paths to /login before the
crawler follows links. Explicitly listing the route in kit.prerender.entries
tells SvelteKit to render it directly without crawling.
Also removes a misleading comment that claimed the auth hook guards
prerendered static files — it does not. Prerendered HTML is served as a
static file by the reverse proxy; hooks.server.ts only runs for SSR requests.
Closes#472
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Felix and Elicit both flagged that the isReader formula had no
in-code explanation at the point of definition; future maintainers
adding a new permission level need a fast pointer to the architectural
rationale.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a readerData fixture and five render-level assertions: the three
ReaderStatsStrip totals, the recent-docs heading, the absent
contributor mission caption, and the drafts module appearing only when
canBlogWrite is true.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>