import { describe, it, expect } from 'vitest'; import { renderToBuffer } from '@react-pdf/renderer'; import { createElement } from 'react'; import { DashboardReport } from '@/lib/pdf/reports/dashboard-report'; import type { ReportBranding } from '@/lib/pdf/reports/types'; const branding: ReportBranding = { logoUrl: null, primaryColor: '#0F4C81', portName: 'Port Nimara', }; describe('PDF report renderer', () => { it('renders a dashboard report with all sections to a non-empty PDF buffer', async () => { const element = createElement(DashboardReport, { title: 'Test report', subtitle: 'Unit-test fixture', branding, generatedAt: '2026-05-21T12:00:00.000Z', config: { kind: 'dashboard', widgetIds: [ 'kpi_overview', 'pipeline_funnel', 'berth_status', 'source_conversion', 'hot_deals', ], }, data: { kpis: { totalClients: 142, activeInterests: 27, pipelineValue: 1250000, pipelineValueCurrency: 'USD', occupancyRate: 64.3, }, pipelineCounts: [ { stage: 'enquiry', count: 12 }, { stage: 'qualified', count: 8 }, { stage: 'eoi', count: 4 }, { stage: 'reservation', count: 2 }, { stage: 'deposit_paid', count: 1 }, ], berthStatus: { total: 120, available: 80, underOffer: 10, maintenance: 5, sold: 25, }, sourceConversion: [ { source: 'website', total: 60, won: 12, lost: 30, conversionRate: 0.2 }, { source: 'referral', total: 25, won: 8, lost: 10, conversionRate: 0.32 }, ], hotDeals: [ { id: 'i1', clientName: 'Acme Corp', mooringNumber: 'A3', stage: 'reservation', lastContact: '2026-05-18T09:00:00.000Z', }, ], }, }); const buf = await renderToBuffer(element as any); expect(buf.byteLength).toBeGreaterThan(2_000); // PDF files start with `%PDF-` magic bytes — sanity-check that // the renderer produced an actual PDF, not an error blob or // empty buffer. const head = buf.subarray(0, 5).toString('utf-8'); expect(head).toBe('%PDF-'); }, 30_000); it('skips sections whose widget id is absent from widgetIds', async () => { const element = createElement(DashboardReport, { title: 'Sparse report', branding, generatedAt: '2026-05-21T12:00:00.000Z', config: { kind: 'dashboard', widgetIds: ['kpi_overview'], }, data: { kpis: { totalClients: 5, activeInterests: 1, pipelineValue: 0, pipelineValueCurrency: 'USD', occupancyRate: 0, }, // Provide pipelineCounts even though widgetIds didn't ask for // it — the renderer should still skip the section since it's // gated on widgetIds, not data presence. pipelineCounts: [{ stage: 'enquiry', count: 1 }], }, }); const buf = await renderToBuffer(element as any); expect(buf.byteLength).toBeGreaterThan(1_000); }, 30_000); it('falls back to a stable layout when no logo URL is supplied', async () => { const element = createElement(DashboardReport, { title: 'Logoless', branding: { ...branding, logoUrl: null }, generatedAt: '2026-05-21T12:00:00.000Z', config: { kind: 'dashboard', widgetIds: ['kpi_overview'] }, data: { kpis: { totalClients: 0, activeInterests: 0, pipelineValue: 0, pipelineValueCurrency: 'USD', occupancyRate: 0, }, }, }); const buf = await renderToBuffer(element as any); expect(buf.byteLength).toBeGreaterThan(1_000); }, 30_000); });