/** * Tests del módulo compartido de header/footer PDF — Etapa 4 Sprint 1.5. */ import { describe, it, expect, vi, beforeEach } from 'vitest' import { PDF_COLORS, drawSharedHeader, drawSharedFooter, applyFootersToAllPages } from '@/lib/export/pdf-sections-shared' // ─── Mock DocLike ───────────────────────────────────────────────────────────── function makeMockDoc(pageW = 210, pageH = 297, totalPages = 2) { return { 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(), splitTextToSize: vi.fn().mockImplementation((t: string) => [t]), internal: { getNumberOfPages: vi.fn().mockReturnValue(totalPages), pageSize: { getWidth: vi.fn().mockReturnValue(pageW), getHeight: vi.fn().mockReturnValue(pageH), }, }, } } const BASE_OPTS = { processName: 'Proceso de prueba', simulatedAt: new Date('2026-05-17T10:00:00Z').getTime() } // ─── PDF_COLORS ─────────────────────────────────────────────────────────────── describe('PDF_COLORS', () => { it('expone inqOrange como array RGB válido [245, 152, 69]', () => { expect(PDF_COLORS.inqOrange).toEqual([245, 152, 69]) }) it('NO incluye colores de CONCILIA (#572FA2 → [87, 47, 162])', () => { const all = JSON.stringify(PDF_COLORS) expect(all).not.toContain('87, 47, 162') // #572FA2 morado CONCILIA expect(all).not.toContain('203, 24, 137') // #CB1889 magenta CONCILIA }) it('tiene al menos 8 colores definidos', () => { expect(Object.keys(PDF_COLORS).length).toBeGreaterThanOrEqual(8) }) }) // ─── drawSharedHeader ───────────────────────────────────────────────────────── describe('drawSharedHeader', () => { let doc: ReturnType beforeEach(() => { doc = makeMockDoc() }) it('retorna un número mayor que el margen (20mm) — espacio del header', () => { const y = drawSharedHeader(doc, { ...BASE_OPTS, pageNumber: 1 }) expect(y).toBeGreaterThan(20) }) it('dibuja border-bottom más grueso en página 1 que en páginas siguientes', () => { drawSharedHeader(doc, { ...BASE_OPTS, pageNumber: 1 }) const page1LineWidths = doc.setLineWidth.mock.calls.map((c: number[]) => c[0]) doc.setLineWidth.mockClear() drawSharedHeader(doc, { ...BASE_OPTS, pageNumber: 2 }) const page2LineWidths = doc.setLineWidth.mock.calls.map((c: number[]) => c[0]) // El grosor máximo de página 1 debe ser mayor que el de página 2 expect(Math.max(...page1LineWidths)).toBeGreaterThan(Math.max(...page2LineWidths)) }) it('dibuja el texto "InQ ROI" en la primera llamada a text()', () => { drawSharedHeader(doc, { ...BASE_OPTS, pageNumber: 1 }) const firstTextCall = doc.text.mock.calls[0] expect(firstTextCall[0]).toBe('InQ ROI') }) it('usa inqOrange para el texto del logo', () => { drawSharedHeader(doc, { ...BASE_OPTS, pageNumber: 1 }) // La primera llamada a setTextColor debe ser el naranja InQ const firstColor = doc.setTextColor.mock.calls[0] expect(firstColor).toEqual(PDF_COLORS.inqOrange) }) it('usa doc.internal.pageSize.getWidth() — compatible con landscape', () => { const landscapeDoc = makeMockDoc(297, 210) const y = drawSharedHeader(landscapeDoc, { ...BASE_OPTS, pageNumber: 1 }) expect(landscapeDoc.internal.pageSize.getWidth).toHaveBeenCalled() expect(y).toBeGreaterThan(20) }) }) // ─── drawSharedFooter ───────────────────────────────────────────────────────── describe('drawSharedFooter', () => { let doc: ReturnType beforeEach(() => { doc = makeMockDoc() }) it('incluye "InQ ROI" en el texto del footer', () => { drawSharedFooter(doc, { ...BASE_OPTS, pageNumber: 1, totalPages: 3 }) const textCalls = doc.text.mock.calls.map((c: unknown[]) => c[0]) expect(textCalls.some((t: unknown) => typeof t === 'string' && t.includes('InQ ROI'))).toBe(true) }) it('incluye "Powered by InQuality" en el texto del footer', () => { drawSharedFooter(doc, { ...BASE_OPTS, pageNumber: 1, totalPages: 3 }) const textCalls = doc.text.mock.calls.map((c: unknown[]) => c[0]) expect(textCalls.some((t: unknown) => typeof t === 'string' && t.includes('Powered by InQuality'))).toBe(true) }) it('muestra paginación correcta "Página X de Y"', () => { drawSharedFooter(doc, { ...BASE_OPTS, pageNumber: 2, totalPages: 4 }) const textCalls = doc.text.mock.calls.map((c: unknown[]) => c[0]) expect(textCalls.some((t: unknown) => typeof t === 'string' && t === 'Página 2 de 4')).toBe(true) }) it('NO incluye versión del producto (v0.1.0)', () => { drawSharedFooter(doc, { ...BASE_OPTS, pageNumber: 1, totalPages: 3 }) const allText = doc.text.mock.calls.map((c: unknown[]) => c[0]).join(' ') expect(allText).not.toContain('v0.') expect(allText).not.toContain('v0.1') }) it('usa doc.internal.pageSize.getHeight() para posición inferior', () => { drawSharedFooter(doc, { ...BASE_OPTS, pageNumber: 1, totalPages: 3 }) expect(doc.internal.pageSize.getHeight).toHaveBeenCalled() }) }) // ─── applyFootersToAllPages ─────────────────────────────────────────────────── describe('applyFootersToAllPages', () => { it('llama setPage para cada página del documento', () => { const doc = makeMockDoc(210, 297, 4) applyFootersToAllPages(doc, BASE_OPTS) expect(doc.setPage).toHaveBeenCalledTimes(4) expect(doc.setPage).toHaveBeenNthCalledWith(1, 1) expect(doc.setPage).toHaveBeenNthCalledWith(4, 4) }) })