test(geschichten): cover GeschichteEditor title guard, status mode, pre-fill, payload
10 browser-based component tests: - title-empty disables both DRAFT save buttons - inline title-required error appears after blur - DRAFT mode renders "Entwurf speichern" + "Veröffentlichen" - PUBLISHED mode renders "Speichern" + "Zurück zu Entwurf" - initialPersons / initialDocuments props render as chips on first paint - title input is populated from a geschichte prop - "Entwurf speichern" passes trimmed title + status=DRAFT to onSubmit - "Veröffentlichen" passes status=PUBLISHED - personIds / documentIds from initial props flow through onSubmit Closes Felix's review B1 on PR #382. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
150
frontend/src/lib/components/GeschichteEditor.svelte.spec.ts
Normal file
150
frontend/src/lib/components/GeschichteEditor.svelte.spec.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { cleanup, render } from 'vitest-browser-svelte';
|
||||
import { page, userEvent } from 'vitest/browser';
|
||||
import GeschichteEditor from './GeschichteEditor.svelte';
|
||||
|
||||
const personFactory = (id: string, displayName: string) => ({
|
||||
id,
|
||||
firstName: displayName.split(' ')[0],
|
||||
lastName: displayName.split(' ').slice(1).join(' ') || displayName,
|
||||
displayName,
|
||||
personType: 'PERSON' as const
|
||||
});
|
||||
|
||||
const docFactory = (id: string, title: string, date = '1882-01-01') => ({
|
||||
id,
|
||||
title,
|
||||
documentDate: date,
|
||||
originalFilename: `${title}.pdf`,
|
||||
status: 'UPLOADED' as const,
|
||||
metadataComplete: false,
|
||||
scriptType: 'UNKNOWN' as const,
|
||||
createdAt: '2024-01-01T00:00:00',
|
||||
updatedAt: '2024-01-01T00:00:00'
|
||||
});
|
||||
|
||||
const draftFactory = (overrides: Record<string, unknown> = {}) => ({
|
||||
id: 'g1',
|
||||
title: 'Existing draft',
|
||||
body: '<p>Hello world</p>',
|
||||
status: 'DRAFT' as const,
|
||||
persons: [],
|
||||
documents: [],
|
||||
createdAt: '2024-01-01T00:00:00',
|
||||
updatedAt: '2024-01-01T00:00:00',
|
||||
...overrides
|
||||
});
|
||||
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe('GeschichteEditor — title-required guard', () => {
|
||||
it('disables both DRAFT save buttons when the title is empty', async () => {
|
||||
const onSubmit = vi.fn().mockResolvedValue(undefined);
|
||||
render(GeschichteEditor, { onSubmit });
|
||||
|
||||
const draft = await page.getByRole('button', { name: 'Entwurf speichern' }).element();
|
||||
const publish = await page.getByRole('button', { name: 'Veröffentlichen' }).element();
|
||||
expect(draft).toHaveProperty('disabled', true);
|
||||
expect(publish).toHaveProperty('disabled', true);
|
||||
});
|
||||
|
||||
it('shows the inline error after the title field is blurred while empty', async () => {
|
||||
const onSubmit = vi.fn();
|
||||
render(GeschichteEditor, { onSubmit });
|
||||
|
||||
await userEvent.click(page.getByPlaceholder('Titel der Geschichte'));
|
||||
await userEvent.tab(); // blur
|
||||
await expect.element(page.getByText('Bitte gib einen Titel ein.')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GeschichteEditor — save bar adapts to status', () => {
|
||||
it('renders DRAFT mode buttons when no geschichte prop is supplied', async () => {
|
||||
render(GeschichteEditor, { onSubmit: vi.fn() });
|
||||
await expect
|
||||
.element(page.getByRole('button', { name: 'Entwurf speichern' }))
|
||||
.toBeInTheDocument();
|
||||
await expect.element(page.getByRole('button', { name: 'Veröffentlichen' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders PUBLISHED mode buttons when geschichte.status is PUBLISHED', async () => {
|
||||
render(GeschichteEditor, {
|
||||
geschichte: draftFactory({ status: 'PUBLISHED', publishedAt: '2024-04-01T12:00:00' }),
|
||||
onSubmit: vi.fn()
|
||||
});
|
||||
await expect.element(page.getByRole('button', { name: 'Speichern' })).toBeInTheDocument();
|
||||
await expect
|
||||
.element(page.getByRole('button', { name: 'Zurück zu Entwurf' }))
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GeschichteEditor — pre-fill', () => {
|
||||
it('renders initial persons as chips', async () => {
|
||||
render(GeschichteEditor, {
|
||||
initialPersons: [personFactory('p1', 'Franz Raddatz')],
|
||||
onSubmit: vi.fn()
|
||||
});
|
||||
await expect.element(page.getByText('Franz Raddatz')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders initial documents as chips', async () => {
|
||||
render(GeschichteEditor, {
|
||||
initialDocuments: [docFactory('d1', 'Brief von Eugenie')],
|
||||
onSubmit: vi.fn()
|
||||
});
|
||||
await expect.element(page.getByText(/Brief von Eugenie/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('populates the title input from a geschichte prop', async () => {
|
||||
render(GeschichteEditor, {
|
||||
geschichte: draftFactory({ title: 'My existing story' }),
|
||||
onSubmit: vi.fn()
|
||||
});
|
||||
const input = await page.getByPlaceholder('Titel der Geschichte').element();
|
||||
expect((input as HTMLInputElement).value).toBe('My existing story');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GeschichteEditor — onSubmit payload', () => {
|
||||
it('passes the trimmed title and DRAFT status when "Entwurf speichern" is clicked', async () => {
|
||||
const onSubmit = vi.fn().mockResolvedValue(undefined);
|
||||
render(GeschichteEditor, { onSubmit });
|
||||
|
||||
await userEvent.fill(page.getByPlaceholder('Titel der Geschichte'), ' My title ');
|
||||
await userEvent.click(page.getByRole('button', { name: 'Entwurf speichern' }));
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
const payload = onSubmit.mock.calls[0][0];
|
||||
expect(payload.title).toBe('My title');
|
||||
expect(payload.status).toBe('DRAFT');
|
||||
});
|
||||
|
||||
it('passes status=PUBLISHED when "Veröffentlichen" is clicked', async () => {
|
||||
const onSubmit = vi.fn().mockResolvedValue(undefined);
|
||||
render(GeschichteEditor, { onSubmit });
|
||||
|
||||
await userEvent.fill(page.getByPlaceholder('Titel der Geschichte'), 'Story');
|
||||
await userEvent.click(page.getByRole('button', { name: 'Veröffentlichen' }));
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
expect(onSubmit.mock.calls[0][0].status).toBe('PUBLISHED');
|
||||
});
|
||||
|
||||
it('passes the personIds and documentIds from initial props through onSubmit', async () => {
|
||||
const onSubmit = vi.fn().mockResolvedValue(undefined);
|
||||
render(GeschichteEditor, {
|
||||
initialPersons: [personFactory('p1', 'Franz Raddatz')],
|
||||
initialDocuments: [docFactory('d1', 'Brief A')],
|
||||
onSubmit
|
||||
});
|
||||
|
||||
await userEvent.fill(page.getByPlaceholder('Titel der Geschichte'), 'Story');
|
||||
await userEvent.click(page.getByRole('button', { name: 'Entwurf speichern' }));
|
||||
|
||||
expect(onSubmit).toHaveBeenCalledTimes(1);
|
||||
const payload = onSubmit.mock.calls[0][0];
|
||||
expect(payload.personIds).toEqual(['p1']);
|
||||
expect(payload.documentIds).toEqual(['d1']);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user