mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 20:40:55 +00:00
Completes the writer extraction whose other half landed in fb21ad154.
reports.controller now builds a TabularDoc and calls TabularExportService,
so report-export.service.ts and report-export-request.util.ts are dead and
removed — HEAD was carrying both copies with the controller still on the old
one.
Reports gain CSV for free, and the PDF path now passes buildTabularFallbackPdf
as its fallback: previously it passed none, so a box without Chromium silently
returned PdfRenderService's ~900-character generic text dump instead of a
table. Adds a spec covering the CSV writer's quoting of embedded commas and
double quotes — the reason this uses ExcelJS's csv writer rather than a
hand-rolled join.
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
import { PdfRenderService } from '../billing/documents/pdf-render.service';
|
|
import { TabularDoc, TabularExportService } from './tabular-export.service';
|
|
|
|
/** The PDF path is puppeteer-backed; these specs only cover the sheet writers. */
|
|
const service = new TabularExportService(null as unknown as PdfRenderService);
|
|
|
|
const doc: TabularDoc = {
|
|
title: 'Bookings',
|
|
description: 'every booking',
|
|
label: 'test',
|
|
columns: [
|
|
{ key: 'ref', label: 'Reference', type: 'string' },
|
|
{ key: 'customer', label: 'Customer', type: 'string' },
|
|
{ key: 'amount', label: 'Amount', type: 'money' },
|
|
{ key: 'gov', label: 'Government', type: 'boolean' },
|
|
],
|
|
rows: [
|
|
{ ref: 'BK-1', customer: 'Acme, Inc.', amount: 1234.5, gov: true },
|
|
{ ref: 'BK-2', customer: 'Quote "Q" Ltd', amount: null, gov: false },
|
|
],
|
|
kpis: [{ label: 'Bookings', value: 2 }],
|
|
};
|
|
|
|
describe('TabularExportService.toCsv', () => {
|
|
it('quotes a value containing the delimiter — the reason we do not hand-roll join(",")', async () => {
|
|
const csv = (await service.toCsv(doc)).toString('utf8');
|
|
expect(csv).toContain('"Acme, Inc."');
|
|
});
|
|
|
|
it('escapes embedded double quotes by doubling them', async () => {
|
|
const csv = (await service.toCsv(doc)).toString('utf8');
|
|
expect(csv).toContain('"Quote ""Q"" Ltd"');
|
|
});
|
|
|
|
it('starts at the header row — no KPI preamble, so the file parses as a plain table', async () => {
|
|
const csv = (await service.toCsv(doc)).toString('utf8');
|
|
expect(csv.split('\n')[0]).toBe('Reference,Customer,Amount,Government');
|
|
expect(csv).not.toContain('Bookings: 2');
|
|
});
|
|
|
|
it('emits one line per row plus the header', async () => {
|
|
const csv = (await service.toCsv(doc)).toString('utf8');
|
|
expect(csv.trim().split('\n').filter(Boolean)).toHaveLength(3);
|
|
});
|
|
|
|
it('only the selected columns are written, in the order given', async () => {
|
|
const csv = (
|
|
await service.toCsv({ ...doc, columns: [doc.columns[2], doc.columns[0]] })
|
|
).toString('utf8');
|
|
expect(csv.split('\n')[0]).toBe('Amount,Reference');
|
|
});
|
|
});
|
|
|
|
describe('TabularExportService.toXlsx', () => {
|
|
it('writes a real xlsx (a zip, so it starts with the PK magic bytes)', async () => {
|
|
const buffer = await service.toXlsx(doc);
|
|
expect(buffer.subarray(0, 2).toString('utf8')).toBe('PK');
|
|
expect(buffer.length).toBeGreaterThan(1000);
|
|
});
|
|
|
|
it('a title longer than Excel\'s 31-char sheet-name limit does not throw', async () => {
|
|
const longTitle = 'A'.repeat(60);
|
|
await expect(service.toXlsx({ ...doc, title: longTitle })).resolves.toBeInstanceOf(Buffer);
|
|
});
|
|
});
|