import { describe, it, expect, vi, beforeEach } from 'vitest'; import { screen, waitFor, fireEvent } from '@testing-library/react'; import { render } from '../utils'; import { PrintLogModal } from '../../components/PrintLogModal'; import { api } from '../../api/client'; vi.mock('../../api/client', () => ({ api: { getArchiveRuns: vi.fn(), getSettings: vi.fn().mockResolvedValue({}), getAuthStatus: vi.fn().mockResolvedValue({ auth_enabled: false }), }, })); const sampleRuns = { total: 2, items: [ { id: 99, archive_id: 42, print_name: 'Benchy', printer_name: 'X1C-01', printer_id: 3, status: 'failed', started_at: '2026-05-10T10:00:00Z', completed_at: '2026-05-10T10:05:00Z', duration_seconds: 300, filament_type: 'PLA', filament_color: '#FF0000', filament_used_grams: 10.0, cost: 0.25, energy_kwh: 0.05, energy_cost: 0.01, failure_reason: 'Cancelled by user', thumbnail_path: null, created_by_id: 1, created_by_username: 'admin', created_at: '2026-05-10T10:05:00Z', }, { id: 50, archive_id: 42, print_name: 'Benchy', printer_name: 'X1C-01', printer_id: 3, status: 'completed', started_at: '2026-05-01T10:00:00Z', completed_at: '2026-05-01T11:00:00Z', duration_seconds: 3600, filament_type: 'PLA', filament_color: '#FF0000', filament_used_grams: 100.0, cost: 2.5, energy_kwh: 0.2, energy_cost: 0.05, failure_reason: null, thumbnail_path: null, created_by_id: 1, created_by_username: 'admin', created_at: '2026-05-01T11:00:00Z', }, ], }; describe('PrintLogModal', () => { beforeEach(() => { vi.clearAllMocks(); vi.mocked(api.getArchiveRuns).mockResolvedValue(sampleRuns); }); it('renders the archive name in the modal title', async () => { render(); await waitFor(() => { expect(screen.getByText(/Benchy/)).toBeInTheDocument(); }); }); it('falls back to "this archive" when name is null', async () => { render(); await waitFor(() => { expect(screen.getByText(/this archive/i)).toBeInTheDocument(); }); }); it('lists every run with its filament + cost', async () => { render(); await waitFor(() => { expect(screen.getByText(/100\.0 g/)).toBeInTheDocument(); expect(screen.getByText(/10\.0 g/)).toBeInTheDocument(); }); }); it('shows failure_reason under failed runs', async () => { render(); await waitFor(() => { expect(screen.getByText('Cancelled by user')).toBeInTheDocument(); }); }); it('translates camelCase failure_reason keys (#1687 follow-up)', async () => { vi.mocked(api.getArchiveRuns).mockResolvedValue({ total: 1, items: [ { ...sampleRuns.items[0], failure_reason: 'filamentRunout', }, ], }); render(); await waitFor(() => { expect(screen.getByText('Filament runout')).toBeInTheDocument(); }); expect(screen.queryByText('filamentRunout')).not.toBeInTheDocument(); }); it('shows the empty state when there are no runs', async () => { vi.mocked(api.getArchiveRuns).mockResolvedValue({ total: 0, items: [] }); render(); await waitFor(() => { expect(screen.getByText(/no print events/i)).toBeInTheDocument(); }); }); it('calls onClose when Escape is pressed', async () => { const onClose = vi.fn(); render(); fireEvent.keyDown(window, { key: 'Escape' }); expect(onClose).toHaveBeenCalledTimes(1); }); it('calls onClose when backdrop is clicked', async () => { const onClose = vi.fn(); const { container } = render( ); const backdrop = container.querySelector('.fixed.inset-0'); if (!backdrop) throw new Error('Backdrop not found'); fireEvent.click(backdrop); expect(onClose).toHaveBeenCalledTimes(1); }); it('does not close when clicking inside the modal body', async () => { const onClose = vi.fn(); render(); await waitFor(() => { expect(screen.getByText(/Benchy/)).toBeInTheDocument(); }); // Click on the title text inside the modal fireEvent.click(screen.getByText(/Benchy/)); expect(onClose).not.toHaveBeenCalled(); }); });