Phase 5 Batch 5 Task 5.19: Report Generator Frontend-Oberfläche
- Created frontend/src/api/reports.ts: React Query hooks for templates, presets, generate - Created frontend/src/pages/Reports.tsx: 3-column layout (template list, editor, generate) - Preset quick-action buttons with format selection (PDF/Print/CSV/Excel) - Template editor with Jinja2 code textarea, name, output format selector - JSON data input for report parameters - Download history tracking - Route /reports registered in index.tsx (lazy-loaded) - i18n keys added to de.json and en.json (reports section + nav.reports) - 5 frontend tests: page render, template list, new template, select template, download history - TSC: 0 new errors (2 pre-existing Dms.tsx errors only)
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { ReportsPage } from '../Reports';
|
||||
|
||||
// Mock the API hooks
|
||||
vi.mock('@/api/reports', () => ({
|
||||
useReportTemplates: vi.fn(() => ({
|
||||
data: [
|
||||
{ id: 'tpl-1', name: 'My Report', description: 'Test', template_type: 'jinja2', content: '<html></html>', output_format: 'pdf', created_by: 'user-1' },
|
||||
{ id: 'tpl-2', name: 'CSV Report', description: 'Test CSV', template_type: 'jinja2', content: 'name,email', output_format: 'csv', created_by: 'user-1' },
|
||||
],
|
||||
isLoading: false,
|
||||
})),
|
||||
useReportPresets: vi.fn(() => ({
|
||||
data: [
|
||||
{ key: 'contact_list', name: 'Kontaktliste', description: 'Liste aller Kontakte', icon: 'Users', output_formats: ['pdf', 'print', 'csv', 'excel'] },
|
||||
{ key: 'company_list', name: 'Firmenliste', description: 'Liste aller Firmen', icon: 'Building2', output_formats: ['pdf', 'print', 'csv', 'excel'] },
|
||||
],
|
||||
})),
|
||||
useCreateReportTemplate: vi.fn(() => ({
|
||||
mutateAsync: vi.fn().mockResolvedValue({}),
|
||||
isPending: false,
|
||||
})),
|
||||
useUpdateReportTemplate: vi.fn(() => ({
|
||||
mutateAsync: vi.fn().mockResolvedValue({}),
|
||||
isPending: false,
|
||||
})),
|
||||
useDeleteReportTemplate: vi.fn(() => ({
|
||||
mutateAsync: vi.fn().mockResolvedValue({}),
|
||||
isPending: false,
|
||||
})),
|
||||
useGenerateReport: vi.fn(() => ({
|
||||
mutateAsync: vi.fn().mockResolvedValue({ data: new Blob(['test'], { type: 'application/pdf' }) }),
|
||||
isPending: false,
|
||||
})),
|
||||
useGeneratePresetReport: vi.fn(() => ({
|
||||
mutateAsync: vi.fn().mockResolvedValue({ data: new Blob(['test'], { type: 'application/pdf' }) }),
|
||||
isPending: false,
|
||||
})),
|
||||
downloadBlob: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock i18next
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string, fallback?: string) => fallback || key,
|
||||
i18n: { language: 'de' },
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock toast
|
||||
vi.mock('@/components/ui/Toast', () => ({
|
||||
useToast: () => ({
|
||||
toast: { success: vi.fn(), error: vi.fn() },
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock lucide-react icons
|
||||
vi.mock('lucide-react', () => {
|
||||
const Icon = ({ className }: { className?: string }) => <div className={className} data-testid="icon" />;
|
||||
return {
|
||||
BarChart3: Icon,
|
||||
Building2: Icon,
|
||||
Calendar: Icon,
|
||||
CalendarDays: Icon,
|
||||
Download: Icon,
|
||||
FileText: Icon,
|
||||
Loader2: Icon,
|
||||
Plus: Icon,
|
||||
Printer: Icon,
|
||||
Save: Icon,
|
||||
ShieldCheck: Icon,
|
||||
Trash2: Icon,
|
||||
Users: Icon,
|
||||
};
|
||||
});
|
||||
|
||||
// Helper to render with providers
|
||||
function renderWithProviders(ui: React.ReactElement) {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
|
||||
});
|
||||
return render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrowserRouter>
|
||||
{ui}
|
||||
</BrowserRouter>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('ReportsPage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders the reports page with title and preset quick actions', () => {
|
||||
renderWithProviders(<ReportsPage />);
|
||||
expect(screen.getByTestId('reports-page')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('preset-quick-actions')).toBeInTheDocument();
|
||||
// Should have preset buttons for contact_list and company_list
|
||||
expect(screen.getByTestId('preset-contact_list-pdf')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('preset-company_list-pdf')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays templates in the template list', () => {
|
||||
renderWithProviders(<ReportsPage />);
|
||||
expect(screen.getByTestId('template-list-panel')).toBeInTheDocument();
|
||||
expect(screen.getByText('My Report')).toBeInTheDocument();
|
||||
expect(screen.getByText('CSV Report')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('clicking new template button shows editor with default content', () => {
|
||||
renderWithProviders(<ReportsPage />);
|
||||
const newBtn = screen.getByTestId('btn-new-template');
|
||||
fireEvent.click(newBtn);
|
||||
// Editor should be visible with template name input
|
||||
expect(screen.getByTestId('input-template-name')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('textarea-template-content')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('btn-save-template')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('selecting a template loads it into the editor', () => {
|
||||
renderWithProviders(<ReportsPage />);
|
||||
// Click on the first template
|
||||
fireEvent.click(screen.getByText('My Report'));
|
||||
// Editor should show the template name
|
||||
const nameInput = screen.getByTestId('input-template-name') as HTMLInputElement;
|
||||
expect(nameInput.value).toBe('My Report');
|
||||
});
|
||||
|
||||
it('shows download history section', () => {
|
||||
renderWithProviders(<ReportsPage />);
|
||||
expect(screen.getByTestId('download-history')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user