Files
inq-roi-simulador-web/tests/lib/export/pdf-resources-section.test.ts
Marcos Benítez 67e259e6ed feat(sprint-3): biblioteca, recursos TO-BE, ROI mejorado e identidad de marca
Sprint 3 completo — 7 etapas:

- Etapa 1-2: biblioteca de procesos con agrupación por cliente (Dexie v4,
  LibraryPage, library-store, groupId/tags en Process)
- Etapa 3: modal de configuración global por proceso (horizonte, frecuencia,
  inversión, moneda) con validación y persistencia
- Etapa 4: panel de recursos rediseñado con soporte de unidades y minutos
  de utilización (utilizationMinutes + units)
- Etapa 5: recursos en escenario automatizado — motor de simulación,
  UI de edición en ActivityPanel, comparación AS-IS/TO-BE en reporte
- Etapa 6: PDF recursos automatizados, automatedAssignedResources requerido,
  561 tests pasando
- Etapa 7: identidad de marca — AppHeader gradiente naranja/magenta con logo
  InQuality en 5 páginas, favicon con logo real, logo/marca navegan a home

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:49:43 -03:00

166 lines
6.0 KiB
TypeScript

/**
* Tests unitarios de drawResourcesSection (Sprint 2, Etapa 6).
*
* drawResourcesSection toma doc: any y autoTable: Function directamente,
* por lo que no necesita mock de jspdf — se pasa un objeto plano.
*/
import { describe, it, expect, vi, beforeEach } from 'vitest'
import type { ActivitySimResult, Resource, Activity } from '@/domain/types'
// ─── Mock doc (jsPDF API mínima usada por drawResourcesSection) ───────────────
const mockDoc = {
setFont: vi.fn(),
setFontSize: vi.fn(),
setTextColor: vi.fn(),
setDrawColor: vi.fn(),
setLineWidth: vi.fn(),
text: vi.fn(),
line: vi.fn(),
}
// ─── Fixtures ─────────────────────────────────────────────────────────────────
const mockResource: Resource = {
id: 'r1', processId: 'p1', name: 'Analista Senior', type: 'role', costPerHour: 60,
}
const mockActivity: Activity = {
id: 'a1', processId: 'p1', bpmnElementId: 'task1', name: 'Recibir solicitud', type: 'task',
directCostFixed: 600, executionTimeMinutes: 60,
assignedResources: [{ resourceId: 'r1', utilizationMinutes: 60, units: 1 }],
automatable: false, automatedCostFixed: 0, automatedTimeMinutes: 0,
automatedAssignedResources: [{ resourceId: 'r1', utilizationMinutes: 5, units: 3 }],
}
const activityWithResources: ActivitySimResult = {
activityId: 'a1', bpmnElementId: 'task1', activityName: 'Recibir solicitud',
expectedDirectCost: 660, expectedIndirectCost: 0, expectedTotalCost: 660,
percentOfTotal: 100, executionProbability: 1.0, expectedExecutions: 1.0,
executionTimeMinutes: 60,
resourceCostBreakdown: [{ resourceId: 'r1', resourceName: 'Analista Senior', cost: 60 }],
}
const activityWithoutResources: ActivitySimResult = {
activityId: 'a2', bpmnElementId: 'task2', activityName: 'Archivar',
expectedDirectCost: 100, expectedIndirectCost: 0, expectedTotalCost: 100,
percentOfTotal: 0, executionProbability: 1.0, expectedExecutions: 1.0,
executionTimeMinutes: 10, resourceCostBreakdown: [],
}
// ─── Tests ────────────────────────────────────────────────────────────────────
describe('drawResourcesSection', () => {
let autoTableMock: ReturnType<typeof vi.fn>
beforeEach(() => {
vi.clearAllMocks()
autoTableMock = vi.fn()
})
it('no lanza error con actividades que tienen recursos', async () => {
const { drawResourcesSection } = await import('@/lib/export/pdf-sections')
expect(() =>
drawResourcesSection(
mockDoc,
autoTableMock,
{ perActivity: [activityWithResources] },
[mockResource],
[mockActivity],
'USD',
40
)
).not.toThrow()
expect(autoTableMock).toHaveBeenCalledTimes(1)
})
it('no llama a autoTable si todas las actividades tienen resourceCostBreakdown vacío', async () => {
const { drawResourcesSection } = await import('@/lib/export/pdf-sections')
drawResourcesSection(
mockDoc,
autoTableMock,
{ perActivity: [activityWithoutResources] },
[mockResource],
[mockActivity],
'USD',
40
)
expect(autoTableMock).not.toHaveBeenCalled()
})
it('el header de autoTable incluye la moneda correcta', async () => {
const { drawResourcesSection } = await import('@/lib/export/pdf-sections')
drawResourcesSection(
mockDoc,
autoTableMock,
{ perActivity: [activityWithResources] },
[mockResource],
[mockActivity],
'PYG',
40
)
const callArgs = autoTableMock.mock.calls[0][1] as { head: unknown[][] }
const headFlat = JSON.stringify(callArgs.head)
expect(headFlat).toContain('PYG')
})
it('llama a autoTable exactamente una vez aunque haya varias actividades con recursos', async () => {
const { drawResourcesSection } = await import('@/lib/export/pdf-sections')
const secondActivity: ActivitySimResult = {
...activityWithResources,
activityId: 'a3', activityName: 'Evaluar solicitud',
resourceCostBreakdown: [{ resourceId: 'r1', resourceName: 'Analista Senior', cost: 30 }],
}
drawResourcesSection(
mockDoc,
autoTableMock,
{ perActivity: [activityWithResources, secondActivity] },
[mockResource],
[mockActivity],
'USD',
40
)
expect(autoTableMock).toHaveBeenCalledTimes(1)
})
})
// ─── Escenario automatizado usa automatedAssignedResources ────────────────────
describe('drawResourcesSection — escenario automatizado', () => {
let autoTableMock: ReturnType<typeof vi.fn>
beforeEach(() => {
autoTableMock = vi.fn()
})
it('escenario automatizado: pasa utilizationMinutes y units de automatedAssignedResources', async () => {
const { drawResourcesSection } = await import('@/lib/export/pdf-sections')
drawResourcesSection(
mockDoc, autoTableMock,
{ perActivity: [activityWithResources] },
[mockResource], [mockActivity], 'USD', 40,
'automatizado'
)
const body = (autoTableMock.mock.calls[0][1] as any).body as any[][]
const row = body[0]
// Min. (índice 4) y Uds. (índice 5) deben venir de automatedAssignedResources: { utilMin: 5, units: 3 }
expect(row[4].content).toBe('5')
expect(row[5].content).toBe('3')
})
it('escenario actual: pasa utilizationMinutes y units de assignedResources (sin regresión)', async () => {
const { drawResourcesSection } = await import('@/lib/export/pdf-sections')
drawResourcesSection(
mockDoc, autoTableMock,
{ perActivity: [activityWithResources] },
[mockResource], [mockActivity], 'USD', 40,
'actual'
)
const body = (autoTableMock.mock.calls[0][1] as any).body as any[][]
const row = body[0]
// assignedResources: { utilMin: 60, units: 1 }
expect(row[4].content).toBe('60')
expect(row[5].content).toBe('1')
})
})