diff --git a/frontend/src/__tests__/pages/generate.test.tsx b/frontend/src/__tests__/pages/generate.test.tsx
index 4da4885..3fe8962 100644
--- a/frontend/src/__tests__/pages/generate.test.tsx
+++ b/frontend/src/__tests__/pages/generate.test.tsx
@@ -28,6 +28,15 @@ vi.mock('~/api/config', () => ({
},
}));
+vi.mock('~/api/themes', () => ({
+ themesApi: {
+ list: vi.fn(),
+ create: vi.fn(),
+ update: vi.fn(),
+ remove: vi.fn(),
+ },
+}));
+
// Mock the SSE connection utility
vi.mock('~/utils/sse', () => ({
createSSEConnection: vi.fn(),
@@ -36,6 +45,7 @@ vi.mock('~/utils/sse', () => ({
import { synthesesApi } from '~/api/syntheses';
import { settingsApi } from '~/api/settings';
import { configApi } from '~/api/config';
+import { themesApi } from '~/api/themes';
import { createSSEConnection } from '~/utils/sse';
import GenerateSynthesis from '~/pages/GenerateSynthesis';
@@ -44,6 +54,11 @@ const mockedListProviders = vi.mocked(configApi.listProviders);
const mockedGenerate = vi.mocked(synthesesApi.generate);
const mockedProgressUrl = vi.mocked(synthesesApi.progressUrl);
const mockedCreateSSE = vi.mocked(createSSEConnection);
+const mockedListThemes = vi.mocked(themesApi.list);
+
+const MOCK_THEMES = [
+ { id: 'theme-1', name: 'IA', theme: 'Intelligence Artificielle', categories: ['Cat1'], max_items_per_category: 5, max_age_days: 7, summary_length: 2, created_at: '2026-03-01T00:00:00Z', updated_at: '2026-03-01T00:00:00Z' },
+];
afterEach(() => {
vi.clearAllMocks();
@@ -53,6 +68,7 @@ describe('GenerateSynthesis Page', () => {
it('should render launch button with settings info', async () => {
mockedGetSettings.mockResolvedValue(MOCK_SETTINGS);
mockedListProviders.mockResolvedValue(MOCK_PROVIDER_CONFIGS);
+ mockedListThemes.mockResolvedValue(MOCK_THEMES);
renderWithProviders(() => );
@@ -69,6 +85,7 @@ describe('GenerateSynthesis Page', () => {
it('should call POST /syntheses/generate on launch', async () => {
mockedGetSettings.mockResolvedValue(MOCK_SETTINGS);
mockedListProviders.mockResolvedValue(MOCK_PROVIDER_CONFIGS);
+ mockedListThemes.mockResolvedValue(MOCK_THEMES);
mockedGenerate.mockResolvedValue({ job_id: 'job-123' });
mockedProgressUrl.mockReturnValue('/api/v1/syntheses/generate/job-123/progress');
@@ -101,15 +118,16 @@ describe('GenerateSynthesis Page', () => {
it('should show progress bar from SSE events', async () => {
mockedGetSettings.mockResolvedValue(MOCK_SETTINGS);
mockedListProviders.mockResolvedValue(MOCK_PROVIDER_CONFIGS);
+ mockedListThemes.mockResolvedValue(MOCK_THEMES);
mockedGenerate.mockResolvedValue({ job_id: 'job-123' });
mockedProgressUrl.mockReturnValue('/api/v1/syntheses/generate/job-123/progress');
const mockSSEConnection = {
events: vi.fn(() => [
- { type: 'progress' as const, data: { step: 'search', message: 'Recherche...', percent: 25 } },
+ { type: 'progress' as const, data: { step: 'sources', message: 'Analyse des sources...', percent: 25 } },
]),
status: vi.fn(() => 'connected' as const),
- latestProgress: vi.fn(() => ({ step: 'search', message: 'Recherche...', percent: 25 })),
+ latestProgress: vi.fn(() => ({ step: 'sources', message: 'Analyse des sources...', percent: 25 })),
completedSynthesisId: vi.fn(() => null),
errorMessage: vi.fn(() => null),
close: vi.fn(),
@@ -128,22 +146,23 @@ describe('GenerateSynthesis Page', () => {
await waitFor(() => {
expect(screen.getByText('25%')).toBeInTheDocument();
});
- expect(screen.getByText('Recherche...')).toBeInTheDocument();
+ expect(screen.getByText('Analyse des sources...')).toBeInTheDocument();
});
it('should render step checklist with correct states', async () => {
mockedGetSettings.mockResolvedValue(MOCK_SETTINGS);
mockedListProviders.mockResolvedValue(MOCK_PROVIDER_CONFIGS);
+ mockedListThemes.mockResolvedValue(MOCK_THEMES);
mockedGenerate.mockResolvedValue({ job_id: 'job-123' });
mockedProgressUrl.mockReturnValue('/api/v1/syntheses/generate/job-123/progress');
const mockSSEConnection = {
events: vi.fn(() => [
- { type: 'progress' as const, data: { step: 'search', message: 'Recherche...', percent: 25 } },
- { type: 'progress' as const, data: { step: 'scraping', message: 'Verification...', percent: 50 } },
+ { type: 'progress' as const, data: { step: 'sources', message: 'Analyse...', percent: 25 } },
+ { type: 'progress' as const, data: { step: 'websearch', message: 'Recherche web...', percent: 50 } },
]),
status: vi.fn(() => 'connected' as const),
- latestProgress: vi.fn(() => ({ step: 'scraping', message: 'Verification...', percent: 50 })),
+ latestProgress: vi.fn(() => ({ step: 'websearch', message: 'Recherche web...', percent: 50 })),
completedSynthesisId: vi.fn(() => null),
errorMessage: vi.fn(() => null),
close: vi.fn(),
@@ -161,14 +180,11 @@ describe('GenerateSynthesis Page', () => {
await waitFor(() => {
expect(
- screen.getByText("Recherche d'actualites"),
+ screen.getByText('Sources personnalisees'),
).toBeInTheDocument();
});
expect(
- screen.getByText('Verification des sources'),
- ).toBeInTheDocument();
- expect(
- screen.getByText('Redaction des resumes'),
+ screen.getByText('Recherche web'),
).toBeInTheDocument();
expect(screen.getByText('Sauvegarde')).toBeInTheDocument();
});
@@ -176,6 +192,7 @@ describe('GenerateSynthesis Page', () => {
it('should show retry button on error', async () => {
mockedGetSettings.mockResolvedValue(MOCK_SETTINGS);
mockedListProviders.mockResolvedValue(MOCK_PROVIDER_CONFIGS);
+ mockedListThemes.mockResolvedValue(MOCK_THEMES);
mockedGenerate.mockResolvedValue({ job_id: 'job-123' });
mockedProgressUrl.mockReturnValue('/api/v1/syntheses/generate/job-123/progress');
@@ -209,6 +226,7 @@ describe('GenerateSynthesis Page', () => {
it('should show completion message on success', async () => {
mockedGetSettings.mockResolvedValue(MOCK_SETTINGS);
mockedListProviders.mockResolvedValue(MOCK_PROVIDER_CONFIGS);
+ mockedListThemes.mockResolvedValue(MOCK_THEMES);
mockedGenerate.mockResolvedValue({ job_id: 'job-123' });
mockedProgressUrl.mockReturnValue('/api/v1/syntheses/generate/job-123/progress');
diff --git a/frontend/src/__tests__/pages/settings.test.tsx b/frontend/src/__tests__/pages/settings.test.tsx
index d8fee71..cebaccc 100644
--- a/frontend/src/__tests__/pages/settings.test.tsx
+++ b/frontend/src/__tests__/pages/settings.test.tsx
@@ -69,10 +69,10 @@ describe('Settings Page', () => {
renderWithProviders(() => );
await waitFor(() => {
- const themeInput = screen.getByLabelText(
- 'Theme de la recherche',
+ const maxArticlesInput = screen.getByLabelText(
+ 'Articles max par source',
) as HTMLInputElement;
- expect(themeInput.value).toBe('Intelligence Artificielle');
+ expect(maxArticlesInput.value).toBe('3');
});
});
@@ -86,7 +86,7 @@ describe('Settings Page', () => {
await waitFor(() => {
expect(
- screen.getByLabelText('Theme de la recherche'),
+ screen.getByLabelText('Articles max par source'),
).toBeInTheDocument();
});
@@ -113,7 +113,8 @@ describe('Settings Page', () => {
expect(screen.getAllByText('OpenAI').length).toBeGreaterThanOrEqual(1);
// Verify the provider select has the options
- const providerSelect = screen.getByLabelText("Fournisseur d'IA") as HTMLSelectElement;
+ const providerSelect = document.getElementById('aiProvider') as HTMLSelectElement;
+ expect(providerSelect).toBeTruthy();
const options = providerSelect.querySelectorAll('option');
const optionTexts = Array.from(options).map((o) => o.textContent);
expect(optionTexts).toContain('Google Gemini');
@@ -138,9 +139,7 @@ describe('Settings Page', () => {
expect(geminiTexts).toContain('Gemini 2.5 Pro');
// Change provider to OpenAI
- const providerSelect = screen.getByLabelText(
- "Fournisseur d'IA",
- ) as HTMLSelectElement;
+ const providerSelect = document.getElementById('aiProvider') as HTMLSelectElement;
fireEvent.change(providerSelect, { target: { value: 'openai' } });
await waitFor(() => {
@@ -291,36 +290,30 @@ describe('Settings Page', () => {
});
});
- it('should show validation error for empty theme', async () => {
+ it('should show validation error from backend on save', async () => {
mockedGetSettings.mockResolvedValue(MOCK_SETTINGS);
mockedListProviders.mockResolvedValue(mockProvidersWithOpenAI);
mockedListKeys.mockResolvedValue([]);
// Simulate backend returning a validation error
mockedUpdateSettings.mockRejectedValue({
status: 422,
- message: 'Theme is required',
+ message: 'Validation error',
});
renderWithProviders(() => );
await waitFor(() => {
expect(
- screen.getByLabelText('Theme de la recherche'),
+ screen.getByLabelText('Articles max par source'),
).toBeInTheDocument();
});
- // Clear the theme field
- const themeInput = screen.getByLabelText(
- 'Theme de la recherche',
- ) as HTMLInputElement;
- fireEvent.input(themeInput, { target: { value: '' } });
-
// Click save
const saveButton = screen.getByText('Enregistrer les parametres');
fireEvent.click(saveButton);
await waitFor(() => {
- expect(screen.getByText('Theme is required')).toBeInTheDocument();
+ expect(screen.getByText('Validation error')).toBeInTheDocument();
});
});
});