test: migrate remaining 3 $app/forms consumers to shared mock
Completes Phase 1a after the load-bearing ChronikFuerDichBox spec proved the pattern. ChronikFuerDichBox.test and NotificationDropdown.test (rich result-firing interceptors) keep their submit-fired assertions (optimisticMarkRead/MarkAllRead) and use formsMock.setFormResult for the failure branch. NotificationBell.spec used the simpler intercept-only factory and renders no form of its own, so it adopts the shared superset purely as a render-time stub. Part of #560. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,36 +3,12 @@ import { cleanup, render } from 'vitest-browser-svelte';
|
|||||||
import { page } from 'vitest/browser';
|
import { page } from 'vitest/browser';
|
||||||
import ChronikFuerDichBox from './ChronikFuerDichBox.svelte';
|
import ChronikFuerDichBox from './ChronikFuerDichBox.svelte';
|
||||||
import type { NotificationItem } from '$lib/notification/notifications';
|
import type { NotificationItem } from '$lib/notification/notifications';
|
||||||
|
import * as formsMock from '$mocks/$app/forms';
|
||||||
|
|
||||||
const mockFormResult = vi.hoisted(() => ({ type: 'success' as string }));
|
vi.mock('$app/forms', () => ({ ...formsMock }));
|
||||||
|
|
||||||
vi.mock('$app/forms', () => ({
|
|
||||||
enhance(
|
|
||||||
node: HTMLFormElement,
|
|
||||||
submit?: (opts: {
|
|
||||||
formData: FormData;
|
|
||||||
}) => (opts: {
|
|
||||||
result: { type: string; data?: Record<string, unknown> };
|
|
||||||
update: () => Promise<void>;
|
|
||||||
}) => Promise<void>
|
|
||||||
) {
|
|
||||||
const handler = async (e: Event) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const cb = submit?.({ formData: new FormData(node) } as never);
|
|
||||||
if (typeof cb === 'function') {
|
|
||||||
await (
|
|
||||||
cb as (o: { result: typeof mockFormResult; update: () => Promise<void> }) => Promise<void>
|
|
||||||
)({ result: mockFormResult, update: async () => {} });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
node.addEventListener('submit', handler);
|
|
||||||
return { destroy: () => node.removeEventListener('submit', handler) };
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
mockFormResult.type = 'success';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mention = (overrides: Partial<NotificationItem> = {}): NotificationItem => ({
|
const mention = (overrides: Partial<NotificationItem> = {}): NotificationItem => ({
|
||||||
@@ -160,7 +136,7 @@ describe('ChronikFuerDichBox', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('shows an accessible error banner when the dismiss action returns a failure', async () => {
|
it('shows an accessible error banner when the dismiss action returns a failure', async () => {
|
||||||
mockFormResult.type = 'failure';
|
formsMock.setFormResult({ type: 'failure' });
|
||||||
render(ChronikFuerDichBox, {
|
render(ChronikFuerDichBox, {
|
||||||
props: {
|
props: {
|
||||||
unread: [mention({ id: 'err-1' })],
|
unread: [mention({ id: 'err-1' })],
|
||||||
|
|||||||
@@ -2,18 +2,10 @@ import { afterEach, describe, it, expect, vi } from 'vitest';
|
|||||||
import { cleanup, render } from 'vitest-browser-svelte';
|
import { cleanup, render } from 'vitest-browser-svelte';
|
||||||
import type { NotificationItem } from '$lib/notification/notifications';
|
import type { NotificationItem } from '$lib/notification/notifications';
|
||||||
import NotificationBell from './NotificationBell.svelte';
|
import NotificationBell from './NotificationBell.svelte';
|
||||||
|
import * as formsMock from '$mocks/$app/forms';
|
||||||
|
|
||||||
vi.mock('$app/navigation', () => ({ goto: vi.fn(), beforeNavigate: vi.fn() }));
|
vi.mock('$app/navigation', () => ({ goto: vi.fn(), beforeNavigate: vi.fn() }));
|
||||||
vi.mock('$app/forms', () => ({
|
vi.mock('$app/forms', () => ({ ...formsMock }));
|
||||||
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) };
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
const mockNotificationList = vi.hoisted((): { value: NotificationItem[] } => ({ value: [] }));
|
const mockNotificationList = vi.hoisted((): { value: NotificationItem[] } => ({ value: [] }));
|
||||||
|
|
||||||
|
|||||||
@@ -3,41 +3,15 @@ import { cleanup, render } from 'vitest-browser-svelte';
|
|||||||
import { page } from 'vitest/browser';
|
import { page } from 'vitest/browser';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import NotificationDropdown from './NotificationDropdown.svelte';
|
import NotificationDropdown from './NotificationDropdown.svelte';
|
||||||
|
import * as formsMock from '$mocks/$app/forms';
|
||||||
|
|
||||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||||
|
|
||||||
// Configurable result for the enhance mock — tests that need failure set
|
vi.mock('$app/forms', () => ({ ...formsMock }));
|
||||||
// mockFormResult.type = 'failure' before clicking.
|
|
||||||
const mockFormResult = vi.hoisted(() => ({ type: 'success' as string }));
|
|
||||||
|
|
||||||
// Invoke the SubmitFunction and always call the returned result callback with
|
|
||||||
// mockFormResult so tests can exercise both success and failure branches.
|
|
||||||
vi.mock('$app/forms', () => ({
|
|
||||||
enhance(
|
|
||||||
node: HTMLFormElement,
|
|
||||||
submit?: (opts: {
|
|
||||||
formData: FormData;
|
|
||||||
}) => (opts: {
|
|
||||||
result: { type: string; data?: Record<string, unknown> };
|
|
||||||
update: () => Promise<void>;
|
|
||||||
}) => Promise<void>
|
|
||||||
) {
|
|
||||||
const handler = async (e: Event) => {
|
|
||||||
e.preventDefault();
|
|
||||||
const cb = submit?.({ formData: new FormData(node) } as never);
|
|
||||||
if (typeof cb === 'function') {
|
|
||||||
await cb({ result: mockFormResult, update: async () => {} } as never);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
node.addEventListener('submit', handler);
|
|
||||||
return { destroy: () => node.removeEventListener('submit', handler) };
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
mockFormResult.type = 'success'; // reset to default after each test
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const makeNotification = (overrides: Record<string, unknown> = {}) => ({
|
const makeNotification = (overrides: Record<string, unknown> = {}) => ({
|
||||||
@@ -235,7 +209,7 @@ describe('NotificationDropdown', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('shows a role=alert error banner when mark-all-read returns a failure', async () => {
|
it('shows a role=alert error banner when mark-all-read returns a failure', async () => {
|
||||||
mockFormResult.type = 'failure';
|
formsMock.setFormResult({ type: 'failure' });
|
||||||
render(NotificationDropdown, {
|
render(NotificationDropdown, {
|
||||||
props: {
|
props: {
|
||||||
notifications: [makeNotification()],
|
notifications: [makeNotification()],
|
||||||
@@ -372,7 +346,7 @@ describe('NotificationDropdown', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does NOT call onClose or goto when the dismiss action returns a failure', async () => {
|
it('does NOT call onClose or goto when the dismiss action returns a failure', async () => {
|
||||||
mockFormResult.type = 'failure';
|
formsMock.setFormResult({ type: 'failure' });
|
||||||
const onClose = vi.fn();
|
const onClose = vi.fn();
|
||||||
const n = makeNotification({ id: 'n99', actorName: 'Bob' });
|
const n = makeNotification({ id: 'n99', actorName: 'Bob' });
|
||||||
render(NotificationDropdown, {
|
render(NotificationDropdown, {
|
||||||
|
|||||||
Reference in New Issue
Block a user