test(dashboard): add partial-failure resilience test + fix i18n Dok. key

- page.server.spec.ts: new test verifies topPersons=[] when that fetch
  rejects, rest of reader data still loads — addresses @Sara concern
- ReaderPersonChips: replaces hardcoded "Dok." with
  dashboard_reader_doc_count_suffix Paraglide key (de/en/es)
  — addresses @Felix suggestion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-07 22:26:53 +02:00
committed by marcel
parent 518334bc38
commit 797852b494
5 changed files with 41 additions and 1 deletions

View File

@@ -460,6 +460,7 @@
"dashboard_badge_new": "Neu",
"dashboard_badge_updated": "Aktualisiert",
"dashboard_reader_all_stories": "Alle Geschichten →",
"dashboard_reader_doc_count_suffix": "Dok.",
"dashboard_resume_label": "Zuletzt geöffnet:",
"dashboard_resume_fallback": "Unbekanntes Dokument",
"doc_status_placeholder": "Platzhalter",

View File

@@ -460,6 +460,7 @@
"dashboard_badge_new": "New",
"dashboard_badge_updated": "Updated",
"dashboard_reader_all_stories": "All Stories →",
"dashboard_reader_doc_count_suffix": "docs.",
"dashboard_resume_label": "Last opened:",
"dashboard_resume_fallback": "Unknown document",
"doc_status_placeholder": "Placeholder",

View File

@@ -460,6 +460,7 @@
"dashboard_badge_new": "Nuevo",
"dashboard_badge_updated": "Actualizado",
"dashboard_reader_all_stories": "Todas las historias →",
"dashboard_reader_doc_count_suffix": "docs.",
"dashboard_resume_label": "Último abierto:",
"dashboard_resume_fallback": "Documento desconocido",
"doc_status_placeholder": "Marcador",

View File

@@ -45,7 +45,9 @@ const { persons }: Props = $props();
</span>
<span class="flex min-w-0 flex-col">
<span class="text-ink-1 truncate font-serif text-sm">{p.displayName ?? p.lastName}</span>
<span class="font-sans text-xs text-ink-3">{p.documentCount ?? 0} Dok.</span>
<span class="font-sans text-xs text-ink-3"
>{p.documentCount ?? 0} {m.dashboard_reader_doc_count_suffix()}</span
>
</span>
</a>
{/each}

View File

@@ -375,4 +375,39 @@ describe('home page load — reader branch (isReader = !canWrite && !canAnnotate
expect(result.isReader).toBe(false);
});
it('returns topPersons=[] when topPersons fetch fails, rest of data still loads', async () => {
const okStats = {
response: { ok: true, status: 200 },
data: { totalDocuments: 5, totalPersons: 2, totalStories: 1 }
};
const failPersons = Promise.reject(new Error('timeout'));
const okSearch = { response: { ok: true, status: 200 }, data: { items: [] } };
const okStories = { response: { ok: true, status: 200 }, data: [] };
const mockGet = vi
.fn()
.mockResolvedValueOnce({ response: { ok: true, status: 200 }, data: [] }) // initial persons check
.mockResolvedValueOnce(okStats)
.mockReturnValueOnce(failPersons)
.mockResolvedValueOnce(okSearch)
.mockResolvedValueOnce(okStories);
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
typeof createApiClient
>);
const result = await load({
url: makeUrl(),
fetch: vi.fn() as unknown as typeof fetch,
parent: vi
.fn()
.mockResolvedValue({ canWrite: false, canAnnotate: false, canBlogWrite: false })
} as Parameters<typeof load>[0]);
expect(result.isReader).toBe(true);
if (result.isReader) {
expect(result.topPersons).toEqual([]);
expect(result.readerStats?.totalDocuments).toBe(5);
}
});
});