feat: add What's new feature
Build, Test & Push Frontend / quality-check (push) Successful in 25s
Build, Test & Push Frontend / docker (push) Successful in 39s

Dashboard dialog shows unseen news entries. Users mark them seen with "Got it"
or revisit via the /whats-new page. Admins publish, edit, and delete entries
on /admin/news. Full i18n support and test coverage included.
This commit is contained in:
lobo committed 2026-10-01 10:03:39 +02:00
1 parent 79b086c525
commit b811255c16
17 files changed
+690 -1

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
// @vitest-environment jsdom
import {afterEach, describe, expect, it, vi} from 'vitest';
import {cleanup, screen} from '@testing-library/react';
import WhatsNewPage from './WhatsNewPage.jsx';
import {fetchRoutes, json} from '../test/fetchRoutes.js';
import {renderWithProviders} from '../test/renderWithProviders.jsx';
vi.mock('react-i18next', () => import('../test/i18n-mock.js'));
const entry = (id, title) => ({id, titleEn: title, titleDe: null, textEn: `${title} text`, textDe: null,
publishedAt: '2026-10-01T10:00:00Z', seen: false});
const putCalls = () => fetch.mock.calls.filter(([, init]) => init?.method === 'PUT');
describe('WhatsNewPage', () => {
afterEach(() => {
cleanup();
vi.clearAllMocks();
});
it('lists the entries in the given order and marks them seen once', async () => {
fetchRoutes({'GET /api/news': json(200, {results: [entry(2, 'Second'), entry(1, 'First')]}),
'PUT /api/news/seen': json(204)});
renderWithProviders(<WhatsNewPage/>);
await screen.findByText('Second');
expect(screen.getAllByRole('heading', {level: 3}).map((heading) => heading.textContent)).toEqual(['Second', 'First']);
expect(putCalls()).toHaveLength(1);
});
it('says so when there are no entries', async () => {
fetchRoutes({'GET /api/news': json(200, {results: []}), 'PUT /api/news/seen': json(204)});
renderWithProviders(<WhatsNewPage/>);
expect(await screen.findByText('NEWS.EMPTY')).toBeTruthy();
});
it('shows an error and marks nothing when the load fails', async () => {
fetchRoutes({'GET /api/news': json(500, {})});
renderWithProviders(<WhatsNewPage/>);
expect(await screen.findByText('NEWS.LOAD_ERROR')).toBeTruthy();
expect(putCalls()).toEqual([]);
});
});