diff --git a/frontend/src/__tests__/pages/StatsPage.test.tsx b/frontend/src/__tests__/pages/StatsPage.test.tsx index fdeaf501e..cbf179aaa 100644 --- a/frontend/src/__tests__/pages/StatsPage.test.tsx +++ b/frontend/src/__tests__/pages/StatsPage.test.tsx @@ -41,8 +41,70 @@ const mockPrinters = [ ]; const mockArchives = [ - { id: 1, created_at: '2024-01-01T00:00:00Z', print_name: 'Test Print 1' }, - { id: 2, created_at: '2024-01-02T00:00:00Z', print_name: 'Test Print 2' }, + { + id: 1, + created_at: '2024-01-01T10:00:00Z', + started_at: '2024-01-01T10:00:00Z', + completed_at: '2024-01-01T14:30:00Z', + print_name: 'Benchy', + status: 'completed', + printer_id: 1, + filament_type: 'PLA', + filament_color: '#00FF00', + filament_used_grams: 25, + actual_time_seconds: 16200, + print_time_seconds: 15000, + cost: 0.75, + quantity: 1, + }, + { + id: 2, + created_at: '2024-01-02T14:00:00Z', + started_at: '2024-01-02T14:00:00Z', + completed_at: '2024-01-02T22:00:00Z', + print_name: 'Large Vase', + status: 'completed', + printer_id: 1, + filament_type: 'PETG', + filament_color: '#FF0000', + filament_used_grams: 180, + actual_time_seconds: 28800, + print_time_seconds: 27000, + cost: 5.40, + quantity: 1, + }, + { + id: 3, + created_at: '2024-01-03T08:00:00Z', + started_at: '2024-01-03T08:00:00Z', + completed_at: null, + print_name: 'Failed Bracket', + status: 'failed', + printer_id: 2, + filament_type: 'ABS', + filament_color: '#0000FF', + filament_used_grams: 10, + actual_time_seconds: 3600, + print_time_seconds: 7200, + cost: 0.30, + quantity: 1, + }, + { + id: 4, + created_at: '2024-01-03T20:00:00Z', + started_at: '2024-01-03T20:00:00Z', + completed_at: '2024-01-04T02:00:00Z', + print_name: 'Phone Stand', + status: 'completed', + printer_id: 2, + filament_type: 'PLA', + filament_color: '#00FF00', + filament_used_grams: 45, + actual_time_seconds: 21600, + print_time_seconds: 20000, + cost: 1.35, + quantity: 1, + }, ]; const mockSettings = { @@ -60,6 +122,16 @@ const mockFailureAnalysis = { 'First layer adhesion': 3, 'Filament runout': 2, }, + failures_by_filament: { + 'ABS': 3, + 'PLA': 2, + }, + failures_by_printer: { + '1': 2, + '2': 3, + }, + failures_by_hour: {}, + recent_failures: [], trend: [ { week: '2024-W01', failure_rate: 6.0 }, { week: '2024-W02', failure_rate: 5.0 }, @@ -81,7 +153,7 @@ describe('StatsPage', () => { http.get('/api/v1/settings/', () => { return HttpResponse.json(mockSettings); }), - http.get('/api/v1/stats/failure-analysis', () => { + http.get('/api/v1/archives/analysis/failures', () => { return HttpResponse.json(mockFailureAnalysis); }) ); @@ -127,7 +199,7 @@ describe('StatsPage', () => { await waitFor(() => { expect(screen.getByText('Filament Used')).toBeInTheDocument(); - expect(screen.getByText('5.50kg')).toBeInTheDocument(); + expect(screen.getByText('5.5kg')).toBeInTheDocument(); }); }); }); @@ -138,7 +210,7 @@ describe('StatsPage', () => { await waitFor(() => { expect(screen.getByText('Success Rate')).toBeInTheDocument(); - // Success rate should be calculated: 140/150 = 93% + // Success rate: 140/(140+10) = 93% expect(screen.getByText('93%')).toBeInTheDocument(); }); }); @@ -163,14 +235,6 @@ describe('StatsPage', () => { }); describe('widgets', () => { - it('shows filament types widget', async () => { - render(); - - await waitFor(() => { - expect(screen.getByText('Filament Types')).toBeInTheDocument(); - }); - }); - it('shows time accuracy widget', async () => { render(); @@ -186,6 +250,132 @@ describe('StatsPage', () => { expect(screen.getByText('Print Activity')).toBeInTheDocument(); }); }); + + it('shows failure analysis widget', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Failure Analysis')).toBeInTheDocument(); + }); + }); + + it('shows printer stats widget', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Printer Stats')).toBeInTheDocument(); + }); + }); + + it('shows filament trends widget', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Filament Trends')).toBeInTheDocument(); + }); + }); + + it('shows records widget', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Records')).toBeInTheDocument(); + }); + }); + }); + + describe('printer stats sub-cards', () => { + it('shows prints by printer section', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Prints by Printer')).toBeInTheDocument(); + }); + }); + + it('shows print duration section', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Print Duration')).toBeInTheDocument(); + }); + }); + + it('shows print habits section', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Print Habits')).toBeInTheDocument(); + }); + }); + + it('shows print time of day section', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Print Time of Day')).toBeInTheDocument(); + }); + }); + }); + + describe('filament trends sub-cards', () => { + it('shows by material section', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('By Material')).toBeInTheDocument(); + }); + }); + + it('shows success by material section', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Success by Material')).toBeInTheDocument(); + }); + }); + + it('shows color distribution section', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Color Distribution')).toBeInTheDocument(); + }); + }); + }); + + describe('records widget', () => { + it('shows longest print record', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Longest Print')).toBeInTheDocument(); + }); + }); + + it('shows heaviest print record', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Heaviest Print')).toBeInTheDocument(); + }); + }); + + it('shows most expensive record', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Most Expensive')).toBeInTheDocument(); + }); + }); + + it('shows success streak record', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('Success Streak')).toBeInTheDocument(); + }); + }); }); describe('export', () => {