You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
79 lines
4.0 KiB
TypeScript
79 lines
4.0 KiB
TypeScript
// apps/hq/test/preview-sample.test.ts
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
import express from 'express'
|
|
import { openDb } from '../src/db'
|
|
import { seedIfEmpty } from '../src/seed'
|
|
import { createStaff } from '../src/auth'
|
|
import { documentHtml, documentHtmlSample } from '../src/templates'
|
|
import { apiRouter } from '../src/api'
|
|
|
|
const sampleDoc = {
|
|
id: 'd1', docType: 'INVOICE', docNo: 'INV/26-27-0001', fy: '2026-27', clientId: 'c1',
|
|
docDate: '2026-07-13', status: 'draft', refDocId: null, taxablePaise: 10_000_00,
|
|
cgstPaise: 900_00, sgstPaise: 900_00, igstPaise: 0, roundOffPaise: 0, payablePaise: 11_800_00,
|
|
payload: { lines: [], totals: {} },
|
|
} as never
|
|
const sampleClient = { id: 'c1', code: 'X', name: 'X', stateCode: '32', address: '', contacts: [], status: 'active', notes: '' } as never
|
|
|
|
describe('templates: screen paper styles + documentHtmlSample', () => {
|
|
it('documentHtml carries screen-only paper styles (PDF unaffected — print media)', () => {
|
|
expect(documentHtml(sampleDoc, sampleClient, { 'company.name': 'Tecnostac' })).toContain('@media screen')
|
|
})
|
|
it('documentHtmlSample renders letterhead, bank box and given bullets through the one renderer', () => {
|
|
const html = documentHtmlSample(
|
|
{ 'company.name': 'Acme HQ', 'company.bank': 'HDFC ****9', 'company.state_code': '32' },
|
|
{ contentLines: ['Cloud POS', 'GST filing'] },
|
|
)
|
|
expect(html).toContain('Acme HQ')
|
|
expect(html).toContain('HDFC ****9') // bank box shows (sample is a TAX INVOICE)
|
|
expect(html).toContain('Cloud POS')
|
|
expect(html).toContain('GST filing')
|
|
})
|
|
})
|
|
|
|
async function appWith() {
|
|
const db = openDb(':memory:'); await seedIfEmpty(db)
|
|
await createStaff(db, { email: 'owner@test.in', displayName: 'Owner', role: 'owner', password: 'owner-password' })
|
|
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`
|
|
return { server, base }
|
|
}
|
|
|
|
describe('POST /previews/sample', () => {
|
|
let ctx: Awaited<ReturnType<typeof appWith>>
|
|
beforeAll(async () => { ctx = await appWith() })
|
|
afterAll(() => ctx.server.close())
|
|
const login = async () =>
|
|
(await (await fetch(`${ctx.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 call = async (token: string | null, body: unknown) => {
|
|
const res = await fetch(`${ctx.base}/previews/sample`, { method: 'POST', headers: { 'content-type': 'application/json', ...(token ? { authorization: `Bearer ${token}` } : {}) }, body: JSON.stringify(body) })
|
|
return { status: res.status, json: await res.json() as any }
|
|
}
|
|
it('requires auth', async () => { expect((await call(null, {})).status).toBe(401) })
|
|
it('renders unsaved company edits (field keys, merged over saved settings)', async () => {
|
|
const token = await login()
|
|
const r = await call(token, { company: { name: 'Edited Co', bank: 'ICICI ****7' } })
|
|
expect(r.status).toBe(200)
|
|
expect(r.json.html).toContain('Edited Co')
|
|
expect(r.json.html).toContain('ICICI ****7')
|
|
})
|
|
it('renders unsaved template.* edits, per-type title, and a picked logo', async () => {
|
|
const token = await login()
|
|
const r = await call(token, {
|
|
template: { footerNote: 'Live footer note', declaration: 'Live declaration' },
|
|
titles: { INVOICE: 'GST INVOICE' },
|
|
logo: 'data:image/png;base64,LIVELOGO',
|
|
})
|
|
expect(r.json.html).toContain('Live footer note')
|
|
expect(r.json.html).toContain('Live declaration')
|
|
expect(r.json.html).toContain('GST INVOICE')
|
|
expect(r.json.html).toContain('data:image/png;base64,LIVELOGO')
|
|
})
|
|
it('renders unsaved quote-content bullets', async () => {
|
|
const token = await login()
|
|
const r = await call(token, { contentLines: ['Only this bullet'] })
|
|
expect(r.json.html).toContain('Only this bullet')
|
|
})
|
|
})
|