test(geschichte): add 403, catch-path, and CSRF header coverage for StoryDocumentPanel (#795)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-06-11 13:12:43 +02:00
parent 232721214d
commit a6184fa121

View File

@@ -210,6 +210,62 @@ describe('StoryDocumentPanel — add', () => {
m.geschichte_documents_added_announce({ title: 'Brief von Eugenie' })
);
});
it('routes a 403 response through getErrorMessage on POST', async () => {
stubFetch([makeSearchResultItem('d1', 'Brief von Eugenie')], {
ok: false,
status: 403,
body: { code: 'FORBIDDEN' }
});
render(StoryDocumentPanel, defaultProps());
await addViaPicker(/Brief von Eugenie/i);
await expect.element(page.getByRole('alert')).toBeInTheDocument();
const alertText = page.getByRole('alert').element().textContent ?? '';
expect(alertText).not.toBe('');
expect(alertText).not.toContain('FORBIDDEN');
});
it('shows the generic reload message when POST throws a network error', async () => {
stubFetch([makeSearchResultItem('d1', 'Brief von Eugenie')]);
vi.stubGlobal(
'fetch',
vi.fn((input: RequestInfo | URL, init?: RequestInit) => {
if ((init?.method ?? 'GET').toUpperCase() === 'GET') {
return Promise.resolve({
ok: true,
json: () =>
Promise.resolve({ items: [makeSearchResultItem('d1', 'Brief von Eugenie')] })
});
}
return Promise.reject(new Error('Network error'));
})
);
render(StoryDocumentPanel, defaultProps());
await addViaPicker(/Brief von Eugenie/i);
await expect
.element(page.getByRole('alert'))
.toHaveTextContent(m.journey_mutation_error_reload());
});
it('attaches X-XSRF-TOKEN header from cookie on POST', async () => {
document.cookie = 'XSRF-TOKEN=test-csrf-token';
const fetchMock = stubFetch([makeSearchResultItem('d1', 'Brief von Eugenie')], {
ok: true,
body: makeItem('i1', 10, docSummary('d1', 'Brief von Eugenie'))
});
render(StoryDocumentPanel, defaultProps());
await addViaPicker(/Brief von Eugenie/i);
const post = fetchMock.mock.calls.find(([, init]) => init?.method === 'POST');
const headers = post?.[1]?.headers as Headers;
expect(headers.get('X-XSRF-TOKEN')).toBe('test-csrf-token');
document.cookie = 'XSRF-TOKEN=; Max-Age=0';
});
});
describe('StoryDocumentPanel — remove', () => {
@@ -348,4 +404,45 @@ describe('StoryDocumentPanel — remove', () => {
m.geschichte_documents_remove_label({ title: 'Brief von Eugenie' })
);
});
it('shows the generic reload message when DELETE throws a network error', async () => {
vi.stubGlobal(
'fetch',
vi.fn(() => Promise.reject(new Error('Network error')))
);
render(
StoryDocumentPanel,
defaultProps({ items: [makeItem('i1', 10, docSummary('d1', 'Brief von Eugenie'))] })
);
await userEvent.click(
page.getByRole('button', {
name: m.geschichte_documents_remove_label({ title: 'Brief von Eugenie' })
})
);
await expect
.element(page.getByRole('alert'))
.toHaveTextContent(m.journey_mutation_error_reload());
});
it('attaches X-XSRF-TOKEN header from cookie on DELETE', async () => {
document.cookie = 'XSRF-TOKEN=test-csrf-token';
const fetchMock = stubFetch([], { ok: true, body: {} });
render(
StoryDocumentPanel,
defaultProps({ items: [makeItem('i1', 10, docSummary('d1', 'Brief von Eugenie'))] })
);
await userEvent.click(
page.getByRole('button', {
name: m.geschichte_documents_remove_label({ title: 'Brief von Eugenie' })
})
);
const del = fetchMock.mock.calls.find(([, init]) => init?.method === 'DELETE');
const headers = del?.[1]?.headers as Headers;
expect(headers.get('X-XSRF-TOKEN')).toBe('test-csrf-token');
document.cookie = 'XSRF-TOKEN=; Max-Age=0';
});
});