refactor(chronik): replace callback props with form actions in ChronikFuerDichBox
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m20s
CI / OCR Service Tests (pull_request) Successful in 21s
CI / Backend Unit Tests (pull_request) Successful in 3m22s
CI / fail2ban Regex (pull_request) Successful in 46s
CI / Semgrep Security Scan (pull_request) Successful in 19s
CI / Compose Bucket Idempotency (pull_request) Successful in 58s

Dismiss (X) button and mark-all-read button now submit forms to
/aktivitaeten?/dismiss-notification and /aktivitaeten?/mark-all-read respectively.
Props renamed onMarkRead/onMarkAllRead → optimisticMarkRead/optimisticMarkAllRead.

aktivitaeten/+page.svelte drops the now-deleted onMarkRead/onMarkAllRead wrapper functions
and passes notificationStore.optimisticMarkRead/optimisticMarkAllRead directly to the box.

Tests: $app/forms enhance mock added to both spec files so dismiss and mark-all assertions
work synchronously against form-submit events.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-19 23:16:58 +02:00
parent 2c6b59d0c7
commit 23439e581a
4 changed files with 124 additions and 84 deletions

View File

@@ -4,6 +4,17 @@ import { page } from 'vitest/browser';
import ChronikFuerDichBox from './ChronikFuerDichBox.svelte';
import type { NotificationItem } from '$lib/notification/notifications';
vi.mock('$app/forms', () => ({
enhance(node: HTMLFormElement, submit?: (opts: { formData: FormData }) => unknown) {
const handler = (e: Event) => {
e.preventDefault();
submit?.({ formData: new FormData(node) } as never);
};
node.addEventListener('submit', handler);
return { destroy: () => node.removeEventListener('submit', handler) };
}
}));
afterEach(cleanup);
const mention = (overrides: Partial<NotificationItem> = {}): NotificationItem => ({
@@ -22,7 +33,7 @@ const mention = (overrides: Partial<NotificationItem> = {}): NotificationItem =>
describe('ChronikFuerDichBox', () => {
it('renders the inbox-zero state when there are no unread', async () => {
render(ChronikFuerDichBox, {
props: { unread: [], onMarkRead: () => {}, onMarkAllRead: () => {} }
props: { unread: [], optimisticMarkRead: () => {}, optimisticMarkAllRead: () => {} }
});
await expect.element(page.getByText(/keine neuen erwähnungen/i)).toBeVisible();
@@ -34,8 +45,8 @@ describe('ChronikFuerDichBox', () => {
render(ChronikFuerDichBox, {
props: {
unread: [mention(), mention({ id: 'n-2' }), mention({ id: 'n-3' })],
onMarkRead: () => {},
onMarkAllRead: () => {}
optimisticMarkRead: () => {},
optimisticMarkAllRead: () => {}
}
});
@@ -47,8 +58,8 @@ describe('ChronikFuerDichBox', () => {
render(ChronikFuerDichBox, {
props: {
unread: [mention({ id: 'n-m', type: 'MENTION' }), mention({ id: 'n-r', type: 'REPLY' })],
onMarkRead: () => {},
onMarkAllRead: () => {}
optimisticMarkRead: () => {},
optimisticMarkAllRead: () => {}
}
});
@@ -62,8 +73,8 @@ describe('ChronikFuerDichBox', () => {
render(ChronikFuerDichBox, {
props: {
unread: [mention({ actorName: 'Bertha' })],
onMarkRead: () => {},
onMarkAllRead: () => {}
optimisticMarkRead: () => {},
optimisticMarkAllRead: () => {}
}
});
@@ -76,8 +87,8 @@ describe('ChronikFuerDichBox', () => {
render(ChronikFuerDichBox, {
props: {
unread: [mention({ type: 'REPLY', actorName: 'Carl' })],
onMarkRead: () => {},
onMarkAllRead: () => {}
optimisticMarkRead: () => {},
optimisticMarkAllRead: () => {}
}
});
@@ -86,11 +97,11 @@ describe('ChronikFuerDichBox', () => {
.toBeVisible();
});
it('calls onMarkRead with the notification when its dismiss button is clicked', async () => {
const onMarkRead = vi.fn();
it('calls optimisticMarkRead with the notification id when its dismiss button is clicked', async () => {
const optimisticMarkRead = vi.fn();
const item = mention({ id: 'n-7' });
render(ChronikFuerDichBox, {
props: { unread: [item], onMarkRead, onMarkAllRead: () => {} }
props: { unread: [item], optimisticMarkRead, optimisticMarkAllRead: () => {} }
});
const dismiss = document.querySelector(
@@ -98,31 +109,31 @@ describe('ChronikFuerDichBox', () => {
) as HTMLElement;
dismiss.click();
expect(onMarkRead).toHaveBeenCalledWith(item);
expect(optimisticMarkRead).toHaveBeenCalledWith('n-7');
});
it('calls onMarkAllRead when the mark-all-read button is clicked', async () => {
const onMarkAllRead = vi.fn();
it('calls optimisticMarkAllRead when the mark-all-read button is clicked', async () => {
const optimisticMarkAllRead = vi.fn();
render(ChronikFuerDichBox, {
props: {
unread: [mention()],
onMarkRead: () => {},
onMarkAllRead
optimisticMarkRead: () => {},
optimisticMarkAllRead
}
});
const btn = document.querySelector('[data-testid="chronik-mark-all-read"]') as HTMLElement;
btn.click();
expect(onMarkAllRead).toHaveBeenCalledOnce();
expect(optimisticMarkAllRead).toHaveBeenCalledOnce();
});
it('builds a deep-link href to the comment for each notification', async () => {
render(ChronikFuerDichBox, {
props: {
unread: [mention({ documentId: 'doc-x', referenceId: 'ref-y', annotationId: null })],
onMarkRead: () => {},
onMarkAllRead: () => {}
optimisticMarkRead: () => {},
optimisticMarkAllRead: () => {}
}
});