From 1ac44ae7de9a3b50279026bfd73156dba2a2a852 Mon Sep 17 00:00:00 2001 From: Thomas Joise Date: Mon, 13 Jul 2026 14:59:11 +0530 Subject: [PATCH] =?UTF-8?q?test(hq):=20golden=20fidelity=20=E2=80=94=20pre?= =?UTF-8?q?view=20HTML=20byte-identical=20to=20the=20PDF's=20HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- apps/hq/test/preview-fidelity.test.ts | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 apps/hq/test/preview-fidelity.test.ts diff --git a/apps/hq/test/preview-fidelity.test.ts b/apps/hq/test/preview-fidelity.test.ts new file mode 100644 index 0000000..097f7c8 --- /dev/null +++ b/apps/hq/test/preview-fidelity.test.ts @@ -0,0 +1,42 @@ +// apps/hq/test/preview-fidelity.test.ts +import { describe, it, expect, afterAll } from 'vitest' +import express from 'express' +import { openDb } from '../src/db' +import { seedIfEmpty } from '../src/seed' +import { createStaff } from '../src/auth' +import { createClient, getClient } from '../src/repos-clients' +import { createModule, setPrice } from '../src/repos-modules' +import { createDraft } from '../src/repos-documents' +import { documentHtml } from '../src/templates' +import { apiRouter } from '../src/api' + +function companyMap(db: any): Record { + const rows = db.prepare(`SELECT key, value FROM setting WHERE key LIKE 'company.%'`).all() as { key: string; value: string }[] + return Object.fromEntries(rows.map((r) => [r.key, r.value])) +} + +describe('preview fidelity (string identity with the PDF path)', () => { + const db = openDb(':memory:'); seedIfEmpty(db) + createStaff(db, { email: 'owner@test.in', displayName: 'Owner', role: 'owner', password: 'owner-password' }) + const c = createClient(db, 'u1', { name: 'Acme', code: 'ACME', stateCode: '32', address: 'Kochi', gstin: '32ABCDE1234F1Z9' }) + const m = createModule(db, 'u1', { code: 'POS', name: 'POS Billing', quoteContent: ['Cloud POS', 'GST filing'] }) + setPrice(db, 'u1', { moduleId: m.id, kind: 'yearly', pricePaise: 10_000_00, effectiveFrom: '2026-04-01' }) + const app = express(); app.use(express.json()); app.locals['db'] = db; app.use('/api', apiRouter(db)) + const server = app.listen(0) + const base = `http://localhost:${(server.address() as { port: number }).port}/api` + afterAll(() => server.close()) + + it('preview.html === documentHtml the /pdf route would feed renderPdf', async () => { + const input = { docType: 'INVOICE', clientId: c.id, terms: 'Net 15', + lines: [{ moduleId: m.id, qty: 2, kind: 'yearly' }] } + // The fixture draft: exactly what /documents/:id/pdf renders (un-issued draft, docNo=null). + const fixture = createDraft(db, 'u1', input) + const expected = documentHtml(fixture, getClient(db, c.id)!, companyMap(db)) + + const token = (await (await fetch(`${base}/auth/login`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ email: 'owner@test.in', password: 'owner-password' }) })).json() as any).token + const res = await fetch(`${base}/documents/preview`, { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` }, body: JSON.stringify(input) }) + const out = await res.json() as any + + expect(out.html).toBe(expected) // one renderer — no drift possible + }) +})