import { mkdtempSync, rmSync } from 'node:fs'; import { mkdir, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { buildDesktopPdfExportInput } from '../src/pdf-export.js'; import { startServer } from '../src/server.js'; describe('buildDesktopPdfExportInput', () => { let projectsRoot = ''; const projectId = 'proj-pdf-test'; beforeEach(async () => { projectsRoot = mkdtempSync(path.join(tmpdir(), 'od-pdf-export-')); await mkdir(path.join(projectsRoot, projectId, 'deck', 'assets'), { recursive: true }); await writeFile( path.join(projectsRoot, projectId, 'deck', 'index.html'), '
One
', ); }); afterEach(() => { if (projectsRoot) rmSync(projectsRoot, { recursive: true, force: true }); }); it('reads the project file and derives a raw-route baseHref from the file directory', async () => { const input = await buildDesktopPdfExportInput({ daemonUrl: 'http://127.0.0.1:7456', deck: true, fileName: 'deck/index.html', projectId, projectsRoot, title: 'Seed Deck', }); expect(input).toEqual({ baseHref: 'http://127.0.0.1:7456/api/projects/proj-pdf-test/raw/deck/', deck: true, defaultFilename: 'Seed-Deck.pdf', html: '
One
', title: 'Seed Deck', }); }); it('falls back to the file basename when the caller omits a title', async () => { const input = await buildDesktopPdfExportInput({ daemonUrl: 'http://127.0.0.1:7456', deck: false, fileName: 'deck/index.html', projectId, projectsRoot, }); expect(input.title).toBe('index'); expect(input.defaultFilename).toBe('index.pdf'); }); }); describe('POST /api/projects/:id/export/pdf', () => { it('forwards the project HTML file to the configured desktop PDF exporter', async () => { const projectId = `proj-pdf-route-${Date.now()}`; const calls: unknown[] = []; const started = await startServer({ port: 0, returnServer: true, desktopPdfExporter: async (input: unknown) => { calls.push(input); return { ok: true, path: '/tmp/seed.pdf' }; }, }) as { server: { close(cb: () => void): void }; url: string }; try { const createResponse = await fetch(`${started.url}/api/projects`, { body: JSON.stringify({ id: projectId, name: 'PDF route fixture' }), headers: { 'content-type': 'application/json' }, method: 'POST', }); expect(createResponse.status).toBe(200); const writeResponse = await fetch(`${started.url}/api/projects/${encodeURIComponent(projectId)}/files`, { body: JSON.stringify({ content: '
One
', name: 'deck/index.html', }), headers: { 'content-type': 'application/json' }, method: 'POST', }); expect(writeResponse.status).toBe(200); const response = await fetch(`${started.url}/api/projects/${encodeURIComponent(projectId)}/export/pdf`, { body: JSON.stringify({ deck: true, fileName: 'deck/index.html', title: 'Seed Deck' }), headers: { 'content-type': 'application/json' }, method: 'POST', }); expect(response.status).toBe(200); expect(await response.json()).toEqual({ ok: true, path: '/tmp/seed.pdf' }); expect(calls).toEqual([ { baseHref: `${started.url}/api/projects/${encodeURIComponent(projectId)}/raw/deck/`, deck: true, defaultFilename: 'Seed-Deck.pdf', html: '
One
', title: 'Seed Deck', }, ]); } finally { await new Promise((resolve) => started.server.close(resolve)); } }); });