test(coverage): drive browser tests to 80% on all metrics (#496) #505

Merged
marcel merged 189 commits from feat/issue-496-browser-coverage-tests into main 2026-05-11 21:50:39 +02:00

189 Commits

Author SHA1 Message Date
Marcel
7ee50e6541 test(admin): convert .not.toThrow into form-stays-mounted assertion (admin/groups/new)
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m47s
CI / OCR Service Tests (push) Successful in 16s
CI / Unit & Component Tests (pull_request) Failing after 1m53s
CI / OCR Service Tests (pull_request) Successful in 16s
CI / Backend Unit Tests (push) Successful in 4m12s
CI / Backend Unit Tests (pull_request) Successful in 4m11s
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:52:06 +02:00
Marcel
3383f6650c test(admin): convert .not.toThrow into merge-success-banner-absent assertion (admin/tags/[id])
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:51:24 +02:00
Marcel
10151fd01b test(admin): convert .not.toThrow into form-stays-mounted assertion (admin/users/new)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:50:51 +02:00
Marcel
da5da46951 test(geschichten): convert .not.toThrow into person-filter chip rendering assertion
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:50:21 +02:00
Marcel
de59497606 test(persons): convert .not.toThrow self-skip test into Self-letter rendering assertion
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:49:37 +02:00
Marcel
2d4cdfcba4 test(briefwechsel): convert 3 .not.toThrow to localStorage / container assertions
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:49:06 +02:00
Marcel
cc0820d32f test(routes): convert 3 .not.toThrow in home page to main/h1 assertions
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:48:34 +02:00
Marcel
440bf11634 test(admin): replace 1 setTimeout sleep in invites page with vi.waitFor
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:44:32 +02:00
Marcel
52405bd145 test(tags): replace 1 setTimeout sleep in TagTreeNode with vi.waitFor
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:44:00 +02:00
Marcel
a3f5ba55f4 test(briefwechsel): replace 1 setTimeout sleep in CorrespondenzPersonBar with vi.waitFor
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:43:28 +02:00
Marcel
57ac82b823 test(documents): replace 1 setTimeout sleep in bulk-edit page with vi.waitFor
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:42:58 +02:00
Marcel
1b9cefff86 test(transcription): replace 3 setTimeout sleeps in TranscriptionEditView with vi.waitFor
Also replaces a vacuous expect(true).toBe(true) with a real behavioral
assertion that both block texts remain rendered after rerender.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:40:54 +02:00
Marcel
f2a48f0a34 test(documents): replace 2 setTimeout sleeps in [id]/edit page with vi.waitFor
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:39:32 +02:00
Marcel
96366463ab test(document): replace 2 setTimeout sleeps in WhoWhenSection with vi.waitFor
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:38:09 +02:00
Marcel
dedf2ab990 test(routes): drop 2 setTimeout sleeps in AppNav (auto-wait via expect.element)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:36:41 +02:00
Marcel
e67dec709c test(person): replace 7 setTimeout sleeps in AddRelationshipForm with vi.waitFor
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:34:19 +02:00
Marcel
81fe998c17 test(ocr): replace 8 setTimeout sleeps in OcrProgress with vi.waitFor
waitForSource() helper polls for the EventSource constructor effect
to register the mock; assertion blocks use vi.waitFor on the progress
bar / heading / button changes after each SSE event dispatch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:31:49 +02:00
Marcel
4357eb4fac test(register): replace 8 setTimeout sleeps with vi.waitFor on reactive state changes
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:30:44 +02:00
Marcel
c89b9a2d60 test(admin): rewrite admin/system page test with vi.waitFor
Replaces 15 setTimeout sleeps with vi.waitFor on the actual signal
(fetch URL recorded, banner appears, status text rendered) and
switches the default fetch mock from mockResolvedValue to
mockImplementation so each call yields a fresh Response — no more
"body stream already read" unhandled rejections.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:29:07 +02:00
Marcel
b26ae52fbb test(discussion): rewrite MentionEditor test with vi.waitFor
Replaces 16 setTimeout(350ms / 30ms / 50ms) sleeps with vi.waitFor on
the actual signal — popup listbox appearance/disappearance, option
aria-selected state — so the test no longer races the 200ms internal
debounce against the real clock under CI load.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:26:49 +02:00
Marcel
efe86a074b test(admin): rewrite EntityNav flyout tests with behavioral assertions
Replaces the vacuous expect(true).toBe(true) sleep test with a real
flyout-open assertion (role=dialog appears after trigger click) and
turns the Escape-keydown smoke test into a full open→Escape→closed
behavioral test. Routes the Escape event through document (matches
the svelte:document binding) instead of window.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:25:08 +02:00
Marcel
07651c8b1a test(ocr): convert useOcrJob polling tests to fake timers
Replaces 2 setTimeout-based wait() helpers with vi.useFakeTimers() +
vi.advanceTimersByTimeAsync() so the polling-loop tests no longer
race against the real clock under CI load — they instead deterministically
advance the setInterval by the exact poll interval and let microtasks
flush. Also converts the destroy() .not.toThrow smoke into a direct
expect(job.destroy()).toBeUndefined() check.

Per Sara: polling-loop tests are the legitimate case for fake timers
(time progression matters) — exactly the pattern she requested.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:22:58 +02:00
Marcel
d8cca4d87f test(document): rewrite FileSwitcherStrip test with behavioral assertions
Replaces 3 setTimeout sleeps with vi.waitFor on document.activeElement
during keyboard nav, and converts 2 .not.toThrow smoke tests on the
prev/next buttons into no-op assertions: with a single file in the
strip the active chip stays selected and onSelect is not invoked.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:20:31 +02:00
Marcel
9ea1d6f5e1 test(person): rewrite PersonTypeahead test with behavioral assertions
Replaces 3 setTimeout sleeps with vi.waitFor on listbox / aria-expanded
state and converts 2 .not.toThrow smoke tests + 1 vacuous expect(true)
into assertions about the input remaining usable after fetch errors
and Escape on a closed dropdown being a no-op.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:19:04 +02:00
Marcel
37b9d25f65 test(discussion): rewrite CommentThread test with behavioral assertions
Replaces 8 setTimeout sleeps with vi.waitFor on the actual signal
(textarea value, fetch URL recorded, onCountChange call) and converts
3 .not.toThrow smoke tests into behavioural assertions:

- "no onCountChange wired" → asserts initial comment text still renders
- "network error during reload" → asserts empty-hint state is shown
- "non-OK reload" → asserts empty-hint state is shown

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:16:55 +02:00
Marcel
02cddbbba8 test(routes): rewrite DropZone test with behavioral assertions
Replaces 5 setTimeout sleeps with vi.waitFor on the actual class
transition, and converts 6 .not.toThrow smoke tests into assertions
that the validation guard surfaces the expected error message (or
absence thereof). Tightens the dragging-state regex to bg-accent-bg
so it cannot match the idle hover:border-primary substring.

Runtime: faster + deterministic.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:15:30 +02:00
Marcel
e4a610ba48 test(viewer): rewrite PdfViewer test with behavioral assertions
Replaces 6 setTimeout sleeps with vi.waitFor and expect.element
auto-wait, and converts 9 .not.toThrow smoke tests into assertions
on the rendered PDF nav controls (Zurück/Weiter/Vergrößern/Verkleinern)
and the conditional outdated-annotation notice / annotation visibility
toggle. transcribeMode test now mocks the annotations fetch so the
toggle button is actually rendered (annotationCount > 0 guard).

Runtime: 33s → 4.5s.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:13:09 +02:00
Marcel
6e738913f4 test(documents): rewrite list page test with behavioral assertions
Replaces 3 setTimeout sleeps with click + auto-wait / vi.waitFor on
the bulk-edit-all flow, and converts 14 .not.toThrow smoke tests into
behavioral assertions:

- Advanced-filter labels (Schlagworte/Absender/Empfänger/Von/Bis) for
  every hasAdvancedFilters() branch (senderId, from, to, tags)
- Collapsed advanced section when all filters are at falsy defaults
- Search input value reflected via two-way binding
- BulkSelectionBar surfaces count when store has entries
- bulk-edit-all populates selection store on success

Runtime: 48s → 3.8s. Addresses Sara's blockers on PR #505.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:07:35 +02:00
Marcel
af8782c032 test(documents): rewrite [id]/page test with behavioral assertions
Replaces 13 setTimeout sleeps with vi.waitFor and expect.element
auto-wait, and converts 17 .not.toThrow smoke tests into behavioral
assertions that verify what each scenario actually exposes:

- topbar mount + svelte:head title for prop pass-through cases
- Edit anchor surfaced when canWrite=true
- Details drawer open + sender displayName visible for sender data
- panel-close testid for transcribe-mode entry
- OCR progress heading 'OCR läuft' for RUNNING + jobId
- OCR spinner absent for 500 / DONE / PENDING-without-jobId / network-error

Runtime: 34s → 3.5s, no sleeps. Addresses Sara's "118 setTimeout" and
"74 .not.toThrow" blockers on PR #505.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 17:01:58 +02:00
Marcel
450c658f92 test(tag): rename TagParentPicker.svelte.spec.ts to .svelte.test.ts
Fixes Sara's .spec.ts outlier concern on PR #505 — every other new
test file in the coverage push uses .svelte.test.ts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 16:12:56 +02:00
Marcel
73c540e338 ci: drop redundant npm test step, coverage run covers it
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 1m49s
CI / OCR Service Tests (pull_request) Successful in 16s
CI / Backend Unit Tests (push) Successful in 4m10s
CI / Backend Unit Tests (pull_request) Successful in 4m11s
CI / Unit & Component Tests (push) Failing after 2m5s
CI / OCR Service Tests (push) Successful in 16s
The test:coverage step runs the full suite under Istanbul; running
`npm test` first executes every test twice for no extra signal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 15:16:39 +02:00
Marcel
59e47c048c refactor(transcription): extract block CRUD into createTranscriptionBlocks hook
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Failing after 1m39s
CI / OCR Service Tests (pull_request) Successful in 16s
CI / Backend Unit Tests (pull_request) Successful in 4m9s
Pulls the transcription-block state (load, save, delete, reviewToggle,
markAllReviewed, createFromDraw, toggleTrainingLabel, deleteAnnotation
+ derived blockNumbers / hasBlocks / lastEditedAt / annotationReloadKey)
out of documents/[id]/+page.svelte into a reusable factory in
lib/document/transcription/useTranscriptionBlocks.svelte.ts.

The page now reads transcription.blocks / .blockNumbers / .hasBlocks /
.lastEditedAt / .annotationReloadKey reactively and delegates writes
to transcription.{load, save, delete, reviewToggle, markAllReviewed,
createFromDraw, toggleTrainingLabel, deleteAnnotation,
findByAnnotationId, bumpAnnotationReloadKey}. The confirm-then-delete
dialog stays in the page; the hook only handles the data ops.

24 unit tests cover initial state, load (success / non-OK / network /
empty-id), derived state (blockNumbers in sortOrder, lastEditedAt
recent-pick, lastEditedAt-null fallback), delete (success bumps key /
non-OK throws), reviewToggle (success updates / non-OK no-op), markAll
(success / non-OK), createFromDraw (success / non-OK / network all
return correct shape), toggleTrainingLabel (200 / 500), deleteAnnotation
(linked-block path / orphan-annotation path / orphan-fail throw),
findByAnnotationId match + miss, bumpAnnotationReloadKey.

Also bumps the polling-loop test waits in useOcrJob.svelte.test.ts to
150-200ms (from 60-80ms) so the suite is reliable when run in parallel.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 10:34:14 +02:00
Marcel
3e9d2337e2 refactor(ocr): extract OCR job state machine into createOcrJob hook
Pulls the trigger/poll/check-status state out of documents/[id]/+page.svelte
into a pure factory in lib/ocr/useOcrJob.svelte.ts that takes documentId,
fetchImpl, and onJobFinished callback as injected dependencies.

The page now delegates to ocrJob.triggerOcr / ocrJob.checkStatus /
ocrJob.destroy and reads ocrJob.running / .progressMessage / .errorMessage /
.skippedPages reactively.

Test discipline reset: 22 unit tests cover initial state, triggerOcr 200/
4xx-with-code/4xx-without-code/5xx/network-error paths, useExistingAnnotations
flag round-trip, checkStatus PENDING/RUNNING/DONE/no-jobId/empty-id/5xx/network
paths, polling progressMessage / skippedPages updates, DONE/FAILED → onJobFinished
callback, polling-error swallow, and destroy mid-poll cleanup.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 10:25:26 +02:00
Marcel
3247af41d3 test(viewer): more usePdfRenderer state branch coverage
renderCurrentPage early-returns when canvasEl/textLayerEl null,
init() idempotent on second call, zoomIn after floor, goToPage(1)
no-op.

5 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 09:36:44 +02:00
Marcel
c93d3550eb test(documents): cover ocr-status DONE/no-job/network-error branches
ocr-status DONE without restart polling, no jobId path, fetch
network error caught.

3 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 09:35:03 +02:00
Marcel
0d56913b5a test(routes): cover bulk-edit-all success path + hasAdvancedFilters branches
bulk-edit-all populates the selection store on 200 response,
senderId/from/to truthy paths trigger showAdvanced.

4 new tests covering ~8 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 09:23:30 +02:00
Marcel
5f41c809e6 test(admin): cover all import + thumbnail status branches
FAILED import-status with error message, RUNNING thumbnail with
progress count, RUNNING thumbnail without progress (total=0),
trigger thumbnail backfill, trigger import from idle.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 09:12:17 +02:00
Marcel
6751ede8cf test(discussion): expand CommentThread coverage further
Whitespace-only quotedText not seeded, no onCountChange not provided,
fetch network error during reload, non-OK reload response, own
comment with edit/delete affordances.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 09:04:29 +02:00
Marcel
d297183bb2 test(annotation): expand AnnotationLayer prop-combo coverage
Pointer hover events, multiple annotations with activeAnnotationId,
dimmed-overrides-faded, canDraw delete button, blockNumbers map.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:55:25 +02:00
Marcel
87e87e17f5 test(persons): cover personType icon paths + life-date branches
INSTITUTION/GROUP/UNKNOWN personType icons, birthYear-only and
deathYear-only life-date display.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:50:57 +02:00
Marcel
eed59f50e5 test(geschichten): cover authorName + publishedAt branches
authorName email fallback when no first/last names, undefined-author
empty result, publishedAt missing, body empty no-excerpt, single
person filter render-without-throw.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:49:33 +02:00
Marcel
9571e4a0bf test(documents): cover ocr-status RUNNING path + various doc-prop branches
ocr-status returning RUNNING + jobId triggers pollOcrJob, full
OCR-relevant doc fields, geschichten undefined fallback.

3 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:40:35 +02:00
Marcel
0c05b00df6 test(documents): hit fetch and ocr-status branches in documents/[id] page
Transcription-block fetch failure, fetched blocks rendering,
localStorage overwrite, ocr-status 500 path.

4 new tests covering ~15 branches in transcribe-mode flow.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:34:48 +02:00
Marcel
14d456e3c3 test(routes): expand home page coverage for ?? fallback branches
Adds minimal-data render (default ?? fallbacks), reader-only
minimal data, isReader+canBlogWrite drafts module, full data shape
populated.

4 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:31:22 +02:00
Marcel
eb2b246dd6 test(viewer): cover PdfViewer outdated-annotation notice + fetch errors
Outdated notice when fileHash mismatches, no notice when matching,
fetch error gracefully ignored, non-OK response ignored.

4 new tests covering ~8 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:28:13 +02:00
Marcel
fc2320ccb1 test(relationship): expand AddRelationshipForm coverage
use:enhance vs callback form variant rendering, self-relation
error, submit disabled on missing related person, submit disabled
on yearError.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:24:37 +02:00
Marcel
9598ec32a7 test(tag): expand TagParentPicker keyboard + excludeIds coverage
ArrowUp wrap-around, Escape close, Enter without selection no-op,
keydown without dropdown no-throw, Enter with active selection
selects, excludeIds filter works, parentId fallback as subtitle.

7 new tests covering ~12 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:21:52 +02:00
Marcel
d867ecde78 test(transcription): expand TranscriptionEditView coverage
Adds activeAnnotationId reactive sync, mark-all-reviewed onclick,
all-blocks-rendered, next-block CTA, active vs inactive training
label chip styles.

6 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:17:00 +02:00
Marcel
4d030a9176 test(genealogy): expand StammbaumTree node-rendering branch coverage
Adds selected-node primary fill, birth/death year combinations,
node click and Enter/Space/other-key handling, dashed/solid spouse
line, single-parent connector, focus ring on focus + blur, aria
labels and aria-expanded reflection, accent stripe on selected node.

13 new tests covering ~30 branches in the node-render path.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:15:27 +02:00
Marcel
91d750bc97 test(discussion): expand MentionEditor coverage further
Adds mousedown-to-select flow, Enter-to-select with highlighted
result, fetch network throw handling.

3 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:58:20 +02:00
Marcel
51f86e1fae test(admin): expand admin/invites page coverage
Status color paths (exhausted/expired/revoked), new-invite form
toggle, loadError banner.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:51:35 +02:00
Marcel
659ee485f2 test(primitives): cover Pagination bridge-page and totalPages=0 branches
Bridge-page-replaces-ellipsis paths (left and right), totalPages=0
hide-the-nav.

3 new tests covering ~5 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:46:53 +02:00
Marcel
002ca3bee7 test(admin): cover OcrModelsTable null/em-dash branches
cer/accuracy null → em-dash, cer/accuracy set → percentage,
corrected-lines raw number, multiple rows.

6 tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:41:24 +02:00
Marcel
b31b6dbcfe test(documents): more documents/[id] page coverage with full data shapes
Document with all metadata populated (sender, receivers, tags,
location, scriptType, trainingLabels, geschichten, inferredRelationship,
canBlogWrite, canWrite); URL-driven transcribe mode rendering.

2 new tests covering ~10 branches from prop-driven conditionals.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:37:46 +02:00
Marcel
1301b81d26 test(routes): add documents page input event handlers + bulk store
Adds search input typing, focus/blur events, and rendering with
bulkSelectionStore containing items.

3 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:36:08 +02:00
Marcel
2613380d98 test(admin): expand TagTreeNode coverage
Color dot hidden at depth>0 and when color is null, document count
badge omitted at 0, toggle click mutates collapseMap.

4 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:30:14 +02:00
Marcel
c34295542d test(viewer): add more PdfViewer prop combinations
flashAnnotationId, blockNumbers, activeAnnotationId, combined with
transcribeMode, onAnnotationClick wired in.

5 new tests covering ~10 prop-combo branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:28:02 +02:00
Marcel
b79dcb8df1 test(notification): expand NotificationDropdown coverage
Adds MENTION verb text, REPLY verb/glyph, multiple notifications
rendering.

3 new tests covering ~5 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:23:22 +02:00
Marcel
54e1fd7331 test(admin): expand admin/tags/[id] page coverage
Adds color picker hidden for child tags, form-success default
hidden state, mergeSuccess null render-without-throw.

3 new tests covering ~5 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:18:17 +02:00
Marcel
474779bcc3 test(admin): expand admin/groups/[id] page coverage
Adds banner-hidden defaults, all-8-permission-checkboxes count,
empty permissions array.

4 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:16:36 +02:00
Marcel
44d2fc4b3d test(documents): expand documents/[id]/edit page coverage
Adds doc.title, originalFilename fallback, cancel link href,
form.error pass-through.

4 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:14:42 +02:00
Marcel
02df15c460 test(persons): expand CoCorrespondentsList coverage
Adds single-word name (one-initial) and leading-space edge cases
for the initials function.

2 new tests covering ~4 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:12:19 +02:00
Marcel
1229c80faa test(admin): expand admin/users/[id] page coverage
Adds banner-hidden defaults (success/error), empty groups list,
groups field undefined fallback to [].

4 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:03:22 +02:00
Marcel
baf3dddcb1 test(admin): expand admin/groups/new page coverage
Adds unsaved-warning hidden by default, oninput dirty marker, form
error banner hidden when form is undefined.

3 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:01:28 +02:00
Marcel
b7f9a4dc12 test(viewer): more usePdfRenderer state coverage
Mixed zoomIn/zoomOut return-to-start, zoomOut at floor no-op,
init() resolves and sets pdfjsReady, loadDocument with bogus URL
sets error and flips loading off.

4 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:50:10 +02:00
Marcel
212a885c85 test(briefwechsel): expand briefwechsel page coverage
Adds timeline rendering with documents, persistRecentPerson save
path, malformed-localStorage parse fallback.

3 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:48:52 +02:00
Marcel
575f4dfab0 test(routes): hit truthy/falsy data.X || '' branches in documents page
Adds two tests that pass all filter props as truthy and as falsy
defaults, covering the seed-from-data-or-default branches.

2 new tests covering ~14 branches (all data.X || '' chains).

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:46:40 +02:00
Marcel
e20d40e05f test(document): expand FileSwitcherStrip coverage
Adds prev/next button click safety, ArrowRight + ArrowLeft + ArrowDown
keyboard navigation through chips with wrap-around.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:42:41 +02:00
Marcel
4d8c7c6e4a test(briefwechsel): expand CorrespondenzPersonBar coverage
Adds receiver-focus triggers correspondents fetch, advanced-filter
chevron rotation in both states.

3 new tests covering ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:35:20 +02:00
Marcel
f3c47783c1 test(routes): cover home greeting hour branches
Adds fake-timer tests for morning (h<12), day (12<=h<18), and
evening (h>=18) branches plus the empty-firstName fallback.

4 new tests covering the greeting time-of-day branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:33:58 +02:00
Marcel
82e151c42d test(documents): expand documents/[id] page coverage further
Sender/receivers populated, filePath set, full user object,
Escape vs other keys keydown handler, deep-link comment query.

6 new tests targeting ~14 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:28:31 +02:00
Marcel
38e1726c53 test(admin): expand admin/users/new page coverage
Adds unsaved-warning hidden by default, oninput dirty marker,
form-error banner hidden when form.error undefined.

3 new tests targeting ~6 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:19:34 +02:00
Marcel
0f31e6a5d4 test(primitives): cover DistributionBar zero-total branch
Adds 0/0 zero-total NaN-guard branch and 5/0 outOnly branch.

2 tests covering ~4 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:18:08 +02:00
Marcel
651684e49c test(admin): cover all admin/system status branches
Adds backfill success banner, RUNNING import-status rendering,
DONE import-status with processed count, FAILED thumbnail-status
with error message, DONE thumbnail-status with retry button.

5 new tests covering state-machine branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 05:16:00 +02:00
Marcel
daf1abca28 test(person): cover PersonTypeahead branches
Label + asterisk per required, placeholder prop, initialName seeding,
hidden value input, large/compact class branches, listbox initial
hidden, focus opens listbox with restrictToCorrespondentsOf, ARIA
expanded toggle, Escape keypress safe, fetch error/non-ok branches,
FieldLabelBadge presence, autofocus prop.

17 tests covering ~30 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:57:48 +02:00
Marcel
9795333c2d test(transcription): cover TranscriptionEditView branches
Empty-state coach, review progress counter, mark-all-reviewed
button visibility/disabled state, OcrTrigger conditional, training
labels visible/hidden by canWrite, label toggle callback, blocks
sorted by sortOrder.

12 tests covering ~30 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:51:32 +02:00
Marcel
7c28c0cb4c test(document): cover WhoWhenSection date and location branches
Date invalid state, error visibility before/after input, hidden ISO
output, location with initialLocation, location hidden in editMode,
FieldLabelBadge in editMode, required indicator.

7 tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:42:44 +02:00
Marcel
4e2c5175b5 test(routes): expand documents page coverage
Adds bulk-edit-all click → backend error code branch, fetch throw
fallback message, data.error pass-through to DocumentList, sort/dir
defaults, OR tag operator branch, zoom range pass-through.

7 new tests targeting ~14 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:41:14 +02:00
Marcel
e0eff0a978 test(admin): expand EntityNav coverage
Active-section icon coloring, flyout trigger click, Escape key
handler on document, user/invite count badge rendering.

5 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:32:32 +02:00
Marcel
335dead3b5 test(viewer): expand usePdfRenderer state coverage
Adds renderCurrentPage no-op when pdfjsLib uninitialized, prerender
no-op when pdfDoc null, destroy safe without document, setElements
no-throw, isLoaded false initially, accumulating zoomIn calls.

7 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:30:39 +02:00
Marcel
0a6c9ade0c test(admin): expand admin/system page coverage
Adds backfill-versions and backfill-file-hashes click handlers,
verifies initial fetch hits import-status and thumbnail-status.

3 new tests targeting ~10 branches in the page component.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:29:13 +02:00
Marcel
14993db1f0 test(discussion): expand CommentThread coverage
Adds onCountChange-after-reload (loadOnMount=true), null currentUserId
isOwn behavior, replies flattened in display.

3 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:22:33 +02:00
Marcel
609d1a3450 test(genealogy): cover StammbaumSidePanel branches
Heading from displayName, birth/death years rendering with all
branch combinations, close button, Escape keypress closing,
non-Escape ignored, person detail link, empty placeholder, error
banner on fetch failure, AddRelationshipForm visibility toggle.

12 tests covering ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:20:10 +02:00
Marcel
54d8252499 test(viewer): cover PdfViewer empty and loaded state branches
Empty state when url is empty (no controls, placeholder shown),
loaded state with controls, annotationsDimmed branch, transcribeMode
flag, documentFileHash filtering branch.

6 tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:15:33 +02:00
Marcel
46161fe0ea test(annotation): add full pointer-drag cycles for AnnotationEditOverlay
Adds full drag cycles (down + move + up) for all 8 handles, full
move-area cycle, non-primary pointermove and pointerup ignored,
no-movement pointerup early-return path.

12 new tests covering ~24 additional branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:14:05 +02:00
Marcel
bc24cb3461 test(documents): expand documents/[id] page coverage
Adds doc.title in document title, originalFilename fallback,
'Dokument' default fallback, canWrite=true render, geschichten +
inferredRelationship rendering, doc.id empty handling, task=transcribe
URL parameter render.

7 new tests targeting ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 04:01:46 +02:00
Marcel
a5a8674fd0 test(routes): expand documents page coverage
Adds canWrite=false hides bulk-edit and new-doc CTAs, no-results
empty state, populated document list, q from server, render-without-
throwing for date / tag / sender filters preselected.

7 new tests targeting ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:59:47 +02:00
Marcel
47db30fc97 test(persons): expand persons/[id] page coverage
Co-correspondents derived from received-document senders, self-skip
branch when sender == current person, GeschichtenCard rendered when
geschichten array is non-empty, 5-entry cap on co-correspondents.

4 new tests covering ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:56:08 +02:00
Marcel
68ca7df177 test(routes): expand AppNav coverage for active links and mobile overlay
Active-link styling per pathname (documents, persons, stammbaum,
geschichten, admin), mobile backdrop click closes nav, Escape
keypress on overlay closes nav.

7 new tests targeting ~14 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:53:32 +02:00
Marcel
d51552ed11 test(routes): expand aktivitaeten page coverage
loadError branches (FuerDichBox skipped vs shown), first-run vs
filter-empty empty-state variants, timeline rendering when feed
has items, FilterPills + FuerDichBox conditional render.

7 tests covering ~12 branches in the page file.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:50:13 +02:00
Marcel
b5f8a4e1ac test(routes): expand home page coverage
Adds reader stats rendering and reader grid layout when isReader=true.

2 new tests targeting reader-mode branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:48:55 +02:00
Marcel
bc6489bf2f test(briefwechsel): cover ConversationFilterBar branches
Two persontypeaheads + two date inputs, swap button visible/invisible
based on both persons set, sort label DESC vs ASC, chevron rotation,
onapplyFilters / ontoggleSort / onswapPersons callbacks fire.

11 tests covering ~20 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:47:26 +02:00
Marcel
647624bd2a test(genealogy): expand StammbaumCard coverage
Adds direct-relationship sorting, yearRange formatting (both years,
only fromYear), inferred-relationships disclosure rendering, 5-item
cap on derived relationships.

5 new tests targeting ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:45:50 +02:00
Marcel
2cc06ca506 test(routes): expand register coverage for password and form branches
Adds password show/hide toggle (independent for both fields), pwHint
visible after typing, pwValid green hint for 8+ chars, pwMismatch
red hint, pwMatch green hint, form.error rendering, notifyOnMention
checkbox toggle.

7 new tests targeting ~25 branches in the register flow.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:44:21 +02:00
Marcel
2850206c5f test(discussion): cover CommentThread branches
Empty hint, populated comment list, singular vs plural label,
canComment toggle, showCompose flag with empty/non-empty messages,
quotedText pre-fills textarea, onCountChange on mount, URL routing
for annotation/block/document comment endpoints.

12 tests covering ~25 branches in CommentThread.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:38:47 +02:00
Marcel
860131d1c7 test(discussion): cover MentionEditor branches
Textarea props (placeholder, rows, disabled), popup not shown
initially, popup opens on @ + query, empty results from API,
HTTP error → empty popup, Enter submits when popup closed,
Shift+Enter does not submit, Escape closes popup, Arrow{Up,Down}
navigation, Enter with no results.

12 tests covering ~30 branches in MentionEditor.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:34:18 +02:00
Marcel
fcd26527d0 test(annotation): expand AnnotationLayer coverage
Container style branches (cursor with/without canDraw, touch-action),
drawing pointer flow (canDraw=false skips, pointerdown on existing
annotation skips, no preview when not drawing, pointermove without
draw, pointerup without draw).

7 new tests, +14 covered branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:29:32 +02:00
Marcel
f9b96dac41 test(routes): expand DropZone coverage for drag/drop branches
Adds drag-over and drag-leave styling, drop with no files, multiple
invalid files, mixed valid+invalid files, non-Enter keydown ignore,
window-level dragenter/dragleave with and without 'Files' types,
counter underflow guard.

16 tests, +9 covered branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:27:42 +02:00
Marcel
ab12c15807 test(annotation): expand AnnotationEditOverlay coverage
Adds keyboard navigation (Arrow{Up,Down,Left,Right}, shiftKey step,
non-arrow no-op, edge clamping at all four sides), pointer drag
flows (move-area + each of the 8 handles), early-return branches
for non-primary pointers and pointer events without active drag.

28 tests, +20 covered branches over previous 7-test version.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:17:54 +02:00
Marcel
969828d554 test(activity): cover ChronikEmptyState branches
Three variants (first-run, filter-empty, inbox-zero), title vs body
visibility, data-variant attribute, accent vs ink-3 icon coloring.

5 tests, ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:04:00 +02:00
Marcel
2ee76c4cd8 test(activity): cover DashboardActivityFeed branches
Caption + show-all link, empty/non-empty conditional, actor avatar
vs question-mark fallback, rollup count badge presence, youMentioned
badge, verbMap entries (TEXT_SAVED, FILE_UPLOADED), unknown-kind
fallback, rollup time range, actor name vs initials fallback,
document href construction.

13 tests, ~50 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:02:46 +02:00
Marcel
40ffa03f62 test(activity): cover ChronikFuerDichBox branches
Inbox-zero state, count badge, MENTION vs REPLY glyphs and verbs,
onMarkRead callback per dismiss, onMarkAllRead callback, comment
deep-link href construction.

8 tests, ~30 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 03:01:23 +02:00
Marcel
0482ef322e test(activity): cover ChronikFilterPills branches
Radiogroup with label, all five filter pills, aria-checked for active
filter, tabindex matrix (0 active vs -1 inactive), onChange callback
when clicked.

5 tests, ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:57:36 +02:00
Marcel
6d2196e2ec test(activity): cover ChronikErrorCard and ChronikTimeline
ChronikErrorCard: default vs supplied message, retry callback wiring,
role=alert.

ChronikTimeline: empty render, today bucket grouping, older bucket
grouping, multi-bucket grouping when items span time ranges.

8 tests, ~20 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:45:59 +02:00
Marcel
aa3e46e2f9 test(dashboard): cover DashboardResumeStrip branches
Empty card vs populated strip, title rendering, thumbnail image vs
fallback icon, progress bar aria-valuenow, document detail link,
collaborators stack rendering, safeColor fallback to default when hex
invalid.

9 tests covering ~25 of DashboardResumeStrip's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:41:05 +02:00
Marcel
fd2ff8de7e test(dashboard): cover ReaderDraftsModule branches
Heading, empty placeholder, per-draft row rendering, draft edit link
href, meta line with relative time.

5 tests, ~10 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:37:24 +02:00
Marcel
463920d004 test(dashboard): cover ReaderRecentDocs branches
Heading + all-docs link, New badge gated on createdAt == updatedAt,
sender displayName vs lastName fallback vs em-dash, document detail
link.

8 tests covering ~16 of the recent-docs section's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:35:54 +02:00
Marcel
d4bf39e276 test(dashboard): cover ReaderHeaderBar and ReaderRecentStories
ReaderHeaderBar: time-of-day greeting matrix (Morgen/Mittag/Abend),
welcome with name, stats counts, em-dash for null counts, three
section links.

ReaderRecentStories: empty early return, populated story rows, all-
stories link, story-detail link, body excerpt visibility.

12 tests, ~30 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:34:11 +02:00
Marcel
9695417f26 test(transcription): cover TranscriptionColumn branches
Empty placeholder, heading + per-doc rendering, weekly-pulse visibility
gated on weeklyCount > 0, block-progress label vs em-dash branch,
task=transcribe link, missing-documentDate handling.

8 tests, ~20 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:30:32 +02:00
Marcel
106fbac7db test(routes): cover DocumentList branches
Empty state (default + term-specific), error banner, year groups
default sort, sender-group sort, undated/unknown-sender labels, total
count display. Mocks $app/navigation since the empty-state CTA calls
goto.

8 tests covering ~30 of DocumentList's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:26:48 +02:00
Marcel
ca6937683d test(ocr): cover TrainingHistory branches
Empty placeholder, all four status pill branches (QUEUED/DONE/FAILED/
RUNNING), error-detail disclosure on FAILED, Personalisiert vs Basis
type label, COLLAPSED_COUNT visible runs, person columns visibility
toggle, em-dash CER fallback.

11 tests covering ~25 of TrainingHistory's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:19:35 +02:00
Marcel
5a2034f8e0 test(dashboard): cover ReaderPersonChips branches
Section landmark with aria-label, empty placeholder, per-person chip
with link to detail, document-count chip visibility branch, displayName
vs lastName fallback, all-persons footer link.

7 tests, ~16 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:11:38 +02:00
Marcel
9f7c9cd7c2 test(dashboard): cover DashboardRecentDocuments branches
Empty list early return, heading + per-doc row rendering, title link
href, date visibility tied to updatedAt, stats footnote presence
toggled by stats.totalDocuments.

7 tests covering ~16 of the dashboard section's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:10:03 +02:00
Marcel
1cc9928321 test(discussion): cover CommentMessage branches
Author + initials, comment body, edited label gated on updatedAt vs
createdAt, edit-mode textarea, delete button gated on isOwn, onDelete
callback wiring.

8 tests covering ~16 of CommentMessage's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:08:15 +02:00
Marcel
7d70add709 test(geschichte): cover GeschichtenCard branches
Empty list early return, populated section, write-action link gated on
canWrite, visible-cap of 3, footer show-all link visibility based on
overflow, author name vs email fallback.

9 tests covering ~25 of GeschichtenCard's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 02:02:18 +02:00
Marcel
531d7fb1b9 test(shared/primitives): cover Pagination branches
Hidden when totalPages <= 1, prev/next disabled state matrix at
boundaries, link form when in range, aria-current for active page,
mobile page label, left ellipsis / right ellipsis branches based on
window position, custom ariaLabel.

11 tests covering ~30 of Pagination's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:58:37 +02:00
Marcel
3972c406d1 test(activity): cover ChronikRow variant + kind branches
Avatar with initials vs question-mark fallback, for-you marker
visibility, data-variant matrix (simple/for-you/rollup/comment),
count badge for rollup, comment preview rendering with fallback,
document title link, default vs comment-deep-link href, time-range
label for rollup with happenedAtUntil.

11 tests covering ~40 of ChronikRow's high-uncovered branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:57:05 +02:00
Marcel
d8bd0c3577 test(document): cover DocumentRow branches
Title rendering with originalFilename fallback, sender vs unknown
placeholder, tag buttons per document tag, bulk-select checkbox gated
on canWrite, archive chips visibility, snippet/summary visibility,
em-dash for missing date.

11 tests covering ~30 of the row's branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:48:29 +02:00
Marcel
91daab4151 test(person/relationship): cover AddRelationshipForm branches
Toggle button, form open on click, all relationship type options,
year-error alert when toYear < fromYear, no-error path when equal,
cancel button closes form, onSubmit prop wiring.

7 tests covering ~20 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:43:04 +02:00
Marcel
45c97a84d8 test(person/genealogy): cover StammbaumCard branches
Heading, family-member toggle visibility tied to canWrite, aria-checked
matrix, in-tree banner gating, relationship-error alert, empty
placeholder, no-derived-relationships path, AddRelationshipForm
mounting tied to canWrite.

10 tests covering ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:39:35 +02:00
Marcel
4230f03e0e test(document): cover FileSwitcherStrip branches
Prev/next nav buttons, chip count per file, aria-current matrix for
active id, error-state data attribute, onSelect callback, onRemove
callback, sr-only announcer for active title.

7 tests covering ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:35:23 +02:00
Marcel
ef63ca18d2 test(routes): cover DropZone branches
Drop hint + accepted types render, default no-progress state, invalid
MIME-type rejection, valid PDF acceptance, no-files early return,
click + Enter open the file input, multi-file accept whitelist
attributes.

8 tests covering ~25 of DropZone's 46 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:32:35 +02:00
Marcel
170a770d6f test(admin/tags): cover the tag-edit page branches
Heading with tag name, name input hydration, color picker visible only
for top-level tags, color swatch grid (10 entries), aria-pressed for
active color, success banner branch, error banner branch, merge-success
banner branch.

8 tests covering ~30 branches in the tag-edit page.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:26:14 +02:00
Marcel
54080c14a5 test(documents): smoke-cover the new document page
Renders BulkDocumentEditLayout with prop pass-through and an
empty-values branch.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:15:14 +02:00
Marcel
975394a1cd test(enrich): smoke-cover the enrich document page
Mounts the page, renders the orchestrator, exposes the hidden skip-form,
and renders the three submit-action buttons (skip, save, save+review).

4 tests covering the orchestration entry path of enrich/[id].

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:12:04 +02:00
Marcel
a77a5d008e test(documents): smoke-cover the edit page with confirm-service mock
Renders the document edit page with mocked confirm service. Verifies
DocumentEditLayout mounts, both hidden submit-target forms (review and
delete) exist, and the delete button is present in the action bar.

3 tests covering the orchestration entry path of documents/[id]/edit.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:10:45 +02:00
Marcel
bf1ec51927 test(aktivitaeten): smoke-cover the page with mocked notification store
Mounts the aktivitaeten page with mocks for the notification SSE
singleton (init/destroy/markRead/markAllRead) and $app/state. Verifies
heading renders, error state renders main element, empty state renders
main, and a non-default filter renders without crashing.

4 tests covering the orchestration entry path.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:09:25 +02:00
Marcel
3e86d16976 test(documents): smoke-cover the document detail orchestrator
Mounts the page with mocked $app/state, $app/navigation, and confirm
service. Verifies the top bar renders, the viewer container exists, and
the last-visited localStorage write happens onMount.

3 tests covering the orchestration entry path of the 558-line
documents/[id]/+page.svelte.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:05:47 +02:00
Marcel
07fce7bb44 test(persons): cover the edit page orchestrator branches
Edit heading, persons-section heading, form-error banner branch,
default no-error state, save-bar discard href targets the person
detail. Mocks confirm service.

5 tests, ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 01:00:34 +02:00
Marcel
525479a504 test(routes): cover the home-page (/) dashboard branches
isReader vs contributor dashboard layout switch, greeting visibility tied
to user prop, DropZone rendering gated on canWrite, ReaderDraftsModule
gated on isReader+canBlogWrite, mission-control section caption.

7 tests, ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:56:46 +02:00
Marcel
34f2c1d13e test(admin/ocr): cover index and per-person page branches
admin/ocr index: heading, sender-models heading, global-history link,
defensive defaults for missing trainingInfo fields.

admin/ocr/[personId]: person name from personNames lookup, Unknown
fallback when not found, back-link href, missing-personNames defensive
handling.

8 tests across two pages.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:54:48 +02:00
Marcel
a1a7138ce1 test: cover five small index/empty-state route components
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>
2026-05-10 00:53:14 +02:00
Marcel
acb52c3929 test: cover four small primitives
RelationshipPill: label render, empty-string handling.
OverflowPillDisplay: +N rendering, +0 edge case, aria-hidden marker.
TimelineYAxis: max/0 labels, barAreaHeight inline style, zero handling.
TranscriptionSection: heading + textarea, initial-value hydration,
empty default.

11 tests across four small primitives.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:51:03 +02:00
Marcel
5d89bb4757 test(ocr): cover OcrProgress SSE state branches
Running default render, progress bar element, document event updates
aria-valuenow, done event triggers onDone + clears running view, error
event flips heading, retry button in error state. Mocks EventSource
via Proxy so the SSE effect uses a stub, not a real connection.

6 tests, ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:47:32 +02:00
Marcel
06c11963e9 test(discussion): cover MentionDropdown branches
Listbox label, empty-state placeholder, create-new escape hatch with
noopener target, populated list, default aria-selected on first item,
life-date range visibility, position fallback when clientRect is null,
positioning from clientRect.

8 tests covering ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:43:08 +02:00
Marcel
239d0b27a3 test(admin/tags): cover TagTreeNode recursive branches
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>
2026-05-10 00:40:07 +02:00
Marcel
f0cdd2b305 test(admin): cover the admin entry-page picker branches
Heading, all 5 links when full permissions, per-permission gating
matrix (users+invites, groups, tags, system), entity counts in rows.

7 tests covering ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:38:38 +02:00
Marcel
8f15eea78d test: cover DashboardFamilyPulse and UserMenu branches
DashboardFamilyPulse: null-pulse early return, eyebrow always shown,
headline gated on pages>0, you-line gated on yourPages>0, contributor
chips visibility, count tile rendering.

UserMenu: avatar button vs icon-only branch, aria-expanded matrix,
menu open/close, profile link + logout form rendering, POST/logout
form attributes.

14 tests covering ~30 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:37:15 +02:00
Marcel
9af2b27b83 test(briefwechsel): cover CorrespondenzHero branches
Headline + cross-link, recent-persons divider/chips visibility tied to
list length, onSelectPerson callback wiring, avatar initial uppercase
derivation.

5 tests, ~15 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:34:59 +02:00
Marcel
b7bc0e6eed test: cover UserProfileSection and AccountSection branches
UserProfileSection: four input fields render, prop hydration including
German date conversion, hidden ISO birthDate input, contact textarea
hydration, empty defaults.

AccountSection: heading, email input attributes, password input
attributes.

9 tests across two account-form helpers.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:31:28 +02:00
Marcel
7e86f6e9bc test(admin/system): cover the system page render branches
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>
2026-05-10 00:27:41 +02:00
Marcel
524b09810a test(briefwechsel): cover ConversationTimeline branches
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>
2026-05-10 00:24:39 +02:00
Marcel
19fb05b49c test(briefwechsel): cover CorrespondenzPersonBar branches
Two PersonTypeahead inputs render, swap button visibility tied to
both-persons-set, sort button label/aria-pressed switch on DESC vs ASC,
document count rendering, sort and swap callback wirings.

9 tests covering ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 00:21:35 +02:00
Marcel
9506505675 test(admin): cover EntityNav permission-gated rendering
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>
2026-05-10 00:20:02 +02:00
Marcel
bd88ebdaf7 test(briefwechsel): cover the index page branches
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>
2026-05-09 23:07:58 +02:00
Marcel
329783a2d5 test(admin/tags): cover TagsListPanel branches
Tree label rendering, empty placeholder branch, top-level node
rendering, collapse-button visibility, autocollapse vs manual collapse
via localStorage, expand-on-click flow, localStorage parse path,
malformed-JSON resilience.

9 tests, ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 23:06:32 +02:00
Marcel
4067b2310b test(admin/users): cover UsersListPanel branches
Expanded list, per-user email+fullName rendering with null-name
fallback, group chip rendering, search filter (positive + empty result
branches), aria-current matrix, collapsed view via autocollapse.

8 tests, ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 23:02:43 +02:00
Marcel
8f41fe18bd test(admin/groups): cover GroupsListPanel branches
Expanded list with header, per-group rendering with permission count,
empty placeholder branch, new-group link, aria-current matrix, collapsed
view via autocollapse prop, localStorage preference path, expand-on-click
flow.

8 tests covering ~25 branches (collapsed/expanded × autocollapse ×
localStorage × empty × active matrix).

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 23:01:03 +02:00
Marcel
698e6e0198 test(document): cover TimelineBars branches
One bar per filled bucket, singular vs plural aria-label, aria-pressed
matrix, drag-window visibility tied to isDragging, onbarclick callback,
minimum-height handling for zero-count buckets.

8 tests covering ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:59:27 +02:00
Marcel
49780f205d test(notification): cover NotificationDropdown branches
Dialog with bell label, empty state vs populated list, mark-all-read
visibility branch, REPLY vs MENTION text, unread-dot rendering, all
three callback wirings (onMarkRead, onMarkAllRead, onClose).

10 tests covering the notification dropdown surface.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:53:09 +02:00
Marcel
6302148822 test(documents): cover documents/+ list page render branches
Screen-reader heading, result count visibility tied to totalElements,
new-document CTA gated on canWrite, bulk-edit-all CTA gated on
canWrite × totalElements > 0. Mocks $app/state and $app/navigation.

7 tests covering the orchestration branches without populating items
(DocumentList children require richer DTO shape covered separately).

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:51:31 +02:00
Marcel
fb66ce6284 test: cover register and admin/users/new page branches
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>
2026-05-09 22:38:02 +02:00
Marcel
44903ca742 test(routes): cover AppNav navigation branches
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>
2026-05-09 22:13:46 +02:00
Marcel
907fbf7a15 test(hilfe): cover the Transkriptions-Richtlinien page
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>
2026-05-09 22:10:08 +02:00
Marcel
e43151ceb0 test(admin/groups): cover the group-edit page branches
Heading, name hydration, per-permission checkbox checked-state matrix,
success/error banner branches, cancel link, delete + save buttons.
Mocks confirm service.

7 tests, ~30 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:08:33 +02:00
Marcel
3e4157fa7c test(geschichten): cover the index page branches
Heading, canBlogWrite-gated CTA, no-filter empty state vs for-persons
empty state, all-pill aria-pressed matrix, person-filter chip
rendering, populated card list. Mocks $app/navigation since the filter
buttons call goto.

9 tests, ~25 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:05:32 +02:00
Marcel
9eece5191d test(admin/users): cover the user-edit page branches
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>
2026-05-09 22:02:37 +02:00
Marcel
825437065d test(persons): cover persons/+ list page branches
Heading + stats bar render, empty-state placeholder, populated card
grid, canWrite-gated new-person CTA, search-input hydration from
data.q, document-count chip singular/plural/zero branches, alias
rendering. Mocks $app/navigation since the search debounce calls goto.

10 tests, ~30 branches.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:56:25 +02:00
Marcel
e055e36cae test: cover login and persons/[id] page branches
login: form rendering, registered-success banner branch, form-error
banner branch, form-action wiring, email/password input attributes,
forgot-password link.

persons/[id]: PersonCard heading via prop pass-through, document section
headings, empty-message branches, GeschichtenCard hidden when empty,
co-correspondents derived from sent documents, canWrite gating the edit
link.

14 tests across two large pages.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:54:50 +02:00
Marcel
5dcb535555 test(document): cover TimelineControls and TimelineXAxis branches
TimelineControls: empty render when neither flag is set, reset button
gated on isZoomed, clear button gated on hasSelection, both-on, both
callback wirings.

TimelineXAxis: empty filled → no ticks, populated → ticks render,
omit-year branch when all buckets share a year, show-year branch
across multiple years, length-4 bucket-string fallback.

11 tests across two timeline primitives.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:48:15 +02:00
Marcel
79511ec9a7 test: cover UserPasswordSection and CorrespondenzFilterControls
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>
2026-05-09 21:44:40 +02:00
Marcel
23647d691c test: cover ScriptTypeSelect, SinglePersonHintBar, UserGroupsSection
ScriptTypeSelect: option list, placeholder disabled, value
initialisation, disabled prop propagation.

SinglePersonHintBar: hasDateFilter false vs true branches, sortDir
DESC vs ASC label switch, year-range with only fromDate fallback.

UserGroupsSection: per-group checkbox rendering, label visibility,
selectedGroupIds preselection, empty groups list, default empty
selection.

15 tests across three small primitives.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:31:14 +02:00
Marcel
5361619065 test: cover DocumentThumbnail, UnsavedWarningBanner, PersonsStatsBar
DocumentThumbnail: thumbnailUrl→img branch, no-thumbnail→placeholder
icon branch, sm vs lg size container class, lazy/async loading attrs.

UnsavedWarningBanner: warning text, discard button, callback wiring.

PersonsStatsBar: count rendering, singular/plural label switching for
both persons and documents (4 branches), zero-count plural fallback.

14 tests across three small primitive files.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:27:57 +02:00
Marcel
f490527da2 test(persons): cover PersonEditSaveBar branches
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>
2026-05-09 21:22:32 +02:00
Marcel
cd14f9cd6c test: cover enrich/done and documents/bulk-edit page branches
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>
2026-05-09 21:19:37 +02:00
Marcel
2e9ad7e0be test(shared): cover BackButton and OverflowPillButton
BackButton: visible vs aria-only label branches, custom class
application, history.back() click handler.

OverflowPillButton: +N pill render, aria-expanded matrix
(closed default → open after click), per-person link rendering with
correct href, Escape closes the dropdown.

Both are reused widely; their coverage closes the line and function gap
left after the DocumentTopBar split inflated the denominator.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:14:22 +02:00
Marcel
d6416cf0c9 test(document): cover DocumentMetadataDrawer column branches
Sixteen tests covering the four-column drawer: details column always
renders, persons column branches (no-persons placeholder vs sender
vs receivers), receiver overflow + show-all toggle, tags column
branches (placeholder vs anchor list with /?tag href encoding),
geschichten column visibility (hidden by default, shown for
canBlogWrite, attach link gated on canBlogWrite + documentId, list
rendering, show-all overflow), inferred-relationship pill on the
single-receiver branch.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:11:37 +02:00
Marcel
c80c66a5e1 test: cover geschichten/new and geschichten/[id]/edit page renders
Both pages embed GeschichteEditor (TipTap-based). The tests assert
heading, BackButton presence, no-error default, editor inputs render,
and prop pass-through (initialPersons / initialDocuments). $app/navigation
is mocked because GeschichteEditor pulls beforeNavigate transitively.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:07:12 +02:00
Marcel
3fe52d1e9f test: cover admin/groups/new and enrich/+ page branches
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>
2026-05-09 21:02:30 +02:00
Marcel
a6b1e576de test: cover users/[id], admin/ocr/global, geschichten/[id] page branches
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>
2026-05-09 20:58:25 +02:00
Marcel
e5d51b3a6d test: cover PersonEditForm and SegmentationTrainingCard branches
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>
2026-05-09 20:53:04 +02:00
Marcel
caa215297b refactor(document): extract DocumentTopBarActions from DocumentTopBar
Third Phase 5 split. The desktop action buttons — transcribe,
transcribe-stop, edit link, download link — become their own component
with a focused props interface (documentId, canWrite, isPdf,
transcribeMode bindable, filePath, originalFilename, fileUrl).

TDD: 8 tests covering empty render, transcribe button gating
(canWrite × isPdf × transcribeMode), stop-transcribe rendering, edit
link with documentId href, download link with filePath gating, all
hidden when in transcribe mode. After the test was red the component
was created.

DocumentTopBar dropped from 303 lines to 166. The orchestrator now
just composes BackButton, DocumentTopBarTitle, PersonChipRow,
OverflowPillButton, the details toggle, DocumentTopBarActions,
DocumentMobileMenu, and DocumentMetadataDrawer — each visual region
named in one or two words.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:46:51 +02:00
Marcel
bb3fe10688 refactor(document): extract DocumentMobileMenu from DocumentTopBar
Second step of the Phase 5 split. The kebab dropdown — including
clickOutside handling and its own mobileMenuOpen state — becomes its
own component named after its visual region. The mobile snippet
duplication inside DocumentTopBar is removed; the component owns its
mobile-specific markup.

TDD: DocumentMobileMenu.svelte.test.ts (7 tests) was red first. The
component then made it green (kebab trigger, dropdown open/close on
click, transcribe button gated on canWrite × isPdf × !transcribeMode,
download link gated on filePath). DocumentTopBar wraps the new
component in a md:hidden div so responsive behaviour is unchanged.
Existing 18-test DocumentTopBar suite still passes.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:43:57 +02:00
Marcel
71a44b084c refactor(document): extract DocumentTopBarTitle from DocumentTopBar
First step of the Phase 5 split plan from issue #496. The 14-line title
+ date block becomes its own component named after the visual region.

TDD red/green: DocumentTopBarTitle.svelte.test.ts written first
(7 tests covering title, originalFilename fallback, empty-string
fallback, short-date rendering, no-date branch, title attribute
sourcing). After the test was red the component was created.
DocumentTopBar.svelte updated to use it; the existing 18-test suite
still passes.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:40:26 +02:00
Marcel
b1d7ee1cab test(document): cover DocumentTopBar conditional rendering branches
Eighteen tests covering the user-observable matrix without yet splitting
the component (Phase 5 of the plan): title vs originalFilename fallback,
short-date rendering and absence, transcribe-button gating
(canWrite × isPdf × transcribeMode), edit-link gating, download-link
gating on filePath, kebab-menu visibility on (canWrite & isPdf) || filePath,
details drawer toggle, mobile menu open/close.

The 83 raw branches in the source map mostly to combinations of the
above flags — each test isolates one branch. Per Sara's guidance the
test names read as sentences and verify what the user sees, not internal
state.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:34:27 +02:00
Marcel
bfb0ac6246 test(admin/invites): cover the four invite-status branches and form toggles
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>
2026-05-09 20:29:52 +02:00
Marcel
f812d205c4 test(stammbaum): cover empty/populated/preselect/zoom branches
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>
2026-05-09 20:26:59 +02:00
Marcel
c9389d3b8b test: cover DocumentViewer, PersonalInfoForm, profile page
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>
2026-05-09 20:20:59 +02:00
Marcel
de5763aad2 test(persons): cover PersonDocumentList and persons/new page
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>
2026-05-09 20:18:34 +02:00
Marcel
23fe140087 test: cover CorrespondentSuggestionsDropdown and PersonCard branches
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>
2026-05-09 20:14:16 +02:00
Marcel
151d7e7a29 test: cover OcrTrigger, CoCorrespondentsList, reset-password page
OcrTrigger: select initialisation from storedScriptType (with the
UNKNOWN sentinel collapsing to empty), button disabled-state matrix
across blockCount × scriptType, onTrigger callback wiring, no-annotations
hint visibility.

CoCorrespondentsList: empty-list early return, populated heading + hint,
chip count and links, initials-from-up-to-two-name-parts logic.

reset-password page: form/success branches, hidden-token rendering with
null fallback, MISMATCH vs generic error code mapping, back-to-login
link.

21 tests across three files.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:11:39 +02:00
Marcel
b71448aab4 test: cover PersonTypeBadge, ExpandableText, PersonChipRow branches
PersonTypeBadge: one test per switch arm (INSTITUTION, GROUP, UNKNOWN)
plus the two no-render branches (unrecognised type, empty type).

ExpandableText: clamp detection, toggle visibility logic, expand →
collapse round-trip, default maxLines fallback.

PersonChipRow: sender-only, sender+arrow, abbreviated naming, max-two
visible receivers, +N overflow pill presence/absence, receivers-only
case (no sender → no arrow).

19 tests across three files. Each file uses afterEach(cleanup) and
queries via getByRole/getByText so tests stay decoupled from CSS.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:05:54 +02:00
Marcel
bbc9e64897 test(routes): cover +error and forgot-password page branches
+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>
2026-05-09 20:03:59 +02:00
Marcel
2578da50f9 test(profile,documents): cover PasswordChangeForm and FileSectionNew branches
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>
2026-05-09 20:01:59 +02:00
Marcel
d9270e84a3 test(person): add PersonChip browser tests
Covers the abbreviated/full name branches, the firstName-null fallback
path, link href derivation from person id, initials rendering, and the
deterministic avatar palette colour. Six tests, six branches hit.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 20:00:15 +02:00
Marcel
ee9ce05aee test(document): cover all five DocumentStatusChip status branches
Adds DocumentStatusChip.svelte.test.ts asserting one branch per
DocumentStatus value (PLACEHOLDER, UPLOADED, TRANSCRIBED, REVIEWED,
ARCHIVED) plus the title/aria-label exposure. Each test queries the
element via getByTitle so the component's accessibility surface is
verified at the same time as its branch logic.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:58:35 +02:00
Marcel
0c865883ca test(upload-zone): backfill afterEach(cleanup) for consistent test isolation
UploadZone is the canonical browser-test template referenced from issue #496
implementation guidance. Adding afterEach(cleanup) makes it match the
TranscriptionPanelHeader pattern and prevents cross-test DOM leakage as more
tests are added in this branch.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:56:03 +02:00
Marcel
eb2ec6a71a refactor(test): use getByRole instead of data-testid in TranscriptionPanelHeader test
Per Felix's review on issue #496, tests should query observable behaviour via
ARIA roles, not test-only data-testid attributes. Replaces every
'document.querySelector([data-testid=...])' with 'page.getByRole(...)'.
The disabled-button click test uses force: true so Playwright bypasses its
enabled-check — the behaviour under test is precisely that the click is
ignored.

Refs #496.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:55:05 +02:00
Marcel
891e1f1cf2 chore(routes): delete dev-only demo route
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>
2026-05-09 19:51:51 +02:00