/** * Tests de comportamiento condicional del PDF de escenario con recursos (Sprint 2). * Verifica que la página extra de recursos aparece (o no) según los datos. * * Usa el mismo patrón de mocks que pdf-export.test.ts. */ import { describe, it, expect, vi, beforeEach } from 'vitest' // ─── Mock de jsPDF ──────────────────────────────────────────────────────────── const mockDoc = { setProperties: vi.fn(), setFont: vi.fn(), setFontSize: vi.fn(), setTextColor: vi.fn(), setFillColor: vi.fn(), setDrawColor: vi.fn(), setLineWidth: vi.fn(), text: vi.fn(), rect: vi.fn(), roundedRect: vi.fn(), line: vi.fn(), addImage: vi.fn(), addPage: vi.fn(), setPage: vi.fn(), save: vi.fn(), splitTextToSize: vi.fn().mockImplementation((text: string) => [text]), internal: { getNumberOfPages: vi.fn().mockReturnValue(3), pageSize: { getWidth: vi.fn().mockReturnValue(210), getHeight: vi.fn().mockReturnValue(297), }, }, } vi.mock('jspdf', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any function MockJsPDF(this: any) { return mockDoc } return { jsPDF: MockJsPDF } }) vi.mock('jspdf-autotable', () => ({ default: vi.fn(), })) // ─── Fixtures ───────────────────────────────────────────────────────────────── import type { Process, Simulation, Resource, Activity } from '@/domain/types' const mockProcess: Process = { id: 'p1', name: 'Test Process', clientName: 'Test Client', bpmnXml: '', currency: 'USD', overheadPercentage: 0.2, annualFrequency: 1000, analysisHorizonYears: 3, automationInvestment: 0, createdAt: 0, updatedAt: 0, groupId: null, tags: [], ownerId: 'test-user', updatedBy: 'test-user', orgId: null, } // Simulación SIN recursos en ninguna actividad const mockSimNoResources: Simulation = { id: 'sim1', processId: 'p1', executedAt: new Date('2026-05-13T10:00:00Z').getTime(), result: { totalCost: 1000, totalDirectCost: 800, totalIndirectCost: 200, totalTimeMinutes: 60, perActivity: [{ activityId: 'a1', bpmnElementId: 'task1', activityName: 'Tarea A', expectedDirectCost: 800, expectedIndirectCost: 200, expectedTotalCost: 1000, percentOfTotal: 100, executionProbability: 1.0, expectedExecutions: 1.0, executionTimeMinutes: 60, resourceCostBreakdown: [], }], perResource: [], warnings: [], }, } // Simulación CON recursos en una actividad const mockResource: Resource = { id: 'r1', name: 'Analista', type: 'role', costPerHour: 60, } const mockActivity: Activity = { id: 'a1', processId: 'p1', bpmnElementId: 'task1', name: 'Tarea A', type: 'task', directCostFixed: 200, executionTimeMinutes: 60, assignedResources: [{ resourceId: 'r1', utilizationMinutes: 60, units: 1 }], automatable: false, automatedCostFixed: 0, automatedTimeMinutes: 0, automatedAssignedResources: [], } const mockSimWithResources: Simulation = { id: 'sim2', processId: 'p1', executedAt: new Date('2026-05-13T10:00:00Z').getTime(), result: { totalCost: 1000, totalDirectCost: 800, totalIndirectCost: 200, totalTimeMinutes: 60, perActivity: [{ activityId: 'a1', bpmnElementId: 'task1', activityName: 'Tarea A', expectedDirectCost: 860, expectedIndirectCost: 140, expectedTotalCost: 1000, percentOfTotal: 100, executionProbability: 1.0, expectedExecutions: 1.0, executionTimeMinutes: 60, resourceCostBreakdown: [{ resourceId: 'r1', resourceName: 'Analista', cost: 60 }], }], perResource: [], warnings: [], }, } // ─── Tests ──────────────────────────────────────────────────────────────────── describe('exportToPdf — página condicional de recursos (Sprint 2)', () => { beforeEach(() => { vi.clearAllMocks() }) it('NO llama a drawResourcesSection cuando ninguna actividad tiene recursos', async () => { const autoTable = (await import('jspdf-autotable')).default as ReturnType const { exportToPdf } = await import('@/lib/export/pdf-export') await exportToPdf({ process: mockProcess, simulation: mockSimNoResources, resources: [], heatmapImageData: null, }) // Sin recursos: autoTable se llama exactamente 1 vez (tabla principal de actividades) expect(autoTable).toHaveBeenCalledTimes(1) }) it('llama a autoTable dos veces cuando hay actividades con recursos', async () => { const autoTable = (await import('jspdf-autotable')).default as ReturnType const { exportToPdf } = await import('@/lib/export/pdf-export') await exportToPdf({ process: mockProcess, simulation: mockSimWithResources, resources: [mockResource], heatmapImageData: null, activities: [mockActivity], }) // Con recursos: tabla principal + tabla de recursos = 2 llamadas expect(autoTable).toHaveBeenCalledTimes(2) }) it('addPage se llama una vez más cuando hay recursos (página extra)', async () => { const { exportToPdf } = await import('@/lib/export/pdf-export') await exportToPdf({ process: mockProcess, simulation: mockSimNoResources, resources: [], heatmapImageData: null, }) const addPageCallsWithout = mockDoc.addPage.mock.calls.length vi.clearAllMocks() await exportToPdf({ process: mockProcess, simulation: mockSimWithResources, resources: [mockResource], heatmapImageData: null, activities: [mockActivity], }) const addPageCallsWith = mockDoc.addPage.mock.calls.length expect(addPageCallsWith).toBe(addPageCallsWithout + 1) }) })