refactor: extract buildPdfOptions to DRY up renderPdf/renderUrlPdf (TDD)
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
- Extract shared PDF options construction into buildPdfOptions() - Both renderPdf and renderUrlPdf now use the shared builder - 5 TDD tests added (pdf-options-builder.test.ts) - 633 tests passing, 0 tsc errors
This commit is contained in:
parent
b1a09f7b3f
commit
4e00feb860
3 changed files with 98 additions and 31 deletions
68
src/__tests__/pdf-options-builder.test.ts
Normal file
68
src/__tests__/pdf-options-builder.test.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { buildPdfOptions, PdfRenderOptions } from "../services/browser.js";
|
||||
|
||||
describe("buildPdfOptions", () => {
|
||||
it("returns sensible defaults when no options given", () => {
|
||||
const result = buildPdfOptions({});
|
||||
expect(result).toEqual({
|
||||
format: "A4",
|
||||
landscape: false,
|
||||
printBackground: true,
|
||||
margin: { top: "0", right: "0", bottom: "0", left: "0" },
|
||||
});
|
||||
});
|
||||
|
||||
it("passes through all provided options", () => {
|
||||
const opts: PdfRenderOptions = {
|
||||
format: "Letter",
|
||||
landscape: true,
|
||||
printBackground: false,
|
||||
margin: { top: "10mm", bottom: "10mm" },
|
||||
scale: 1.5,
|
||||
pageRanges: "1-3",
|
||||
preferCSSPageSize: true,
|
||||
width: "210mm",
|
||||
height: "297mm",
|
||||
headerTemplate: "<span>Header</span>",
|
||||
footerTemplate: "<span>Footer</span>",
|
||||
displayHeaderFooter: true,
|
||||
};
|
||||
const result = buildPdfOptions(opts);
|
||||
expect(result.format).toBe("Letter");
|
||||
expect(result.landscape).toBe(true);
|
||||
expect(result.printBackground).toBe(false);
|
||||
expect(result.margin).toEqual({ top: "10mm", bottom: "10mm" });
|
||||
expect(result.scale).toBe(1.5);
|
||||
expect(result.pageRanges).toBe("1-3");
|
||||
expect(result.preferCSSPageSize).toBe(true);
|
||||
expect(result.width).toBe("210mm");
|
||||
expect(result.height).toBe("297mm");
|
||||
expect(result.headerTemplate).toBe("<span>Header</span>");
|
||||
expect(result.footerTemplate).toBe("<span>Footer</span>");
|
||||
expect(result.displayHeaderFooter).toBe(true);
|
||||
});
|
||||
|
||||
it("omits undefined optional fields from output", () => {
|
||||
const result = buildPdfOptions({ format: "A4" });
|
||||
expect(result).not.toHaveProperty("scale");
|
||||
expect(result).not.toHaveProperty("pageRanges");
|
||||
expect(result).not.toHaveProperty("preferCSSPageSize");
|
||||
expect(result).not.toHaveProperty("width");
|
||||
expect(result).not.toHaveProperty("height");
|
||||
expect(result).not.toHaveProperty("headerTemplate");
|
||||
expect(result).not.toHaveProperty("footerTemplate");
|
||||
});
|
||||
|
||||
it("handles printBackground false explicitly", () => {
|
||||
const result = buildPdfOptions({ printBackground: false });
|
||||
expect(result.printBackground).toBe(false);
|
||||
});
|
||||
|
||||
it("defaults displayHeaderFooter to false only when explicitly set", () => {
|
||||
const r1 = buildPdfOptions({});
|
||||
expect(r1).not.toHaveProperty("displayHeaderFooter");
|
||||
|
||||
const r2 = buildPdfOptions({ displayHeaderFooter: false });
|
||||
expect(r2.displayHeaderFooter).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -53,7 +53,10 @@ vi.mock("../services/keys.js", () => {
|
|||
});
|
||||
|
||||
// Mock browser service
|
||||
vi.mock("../services/browser.js", () => ({
|
||||
vi.mock("../services/browser.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<Record<string, unknown>>();
|
||||
return {
|
||||
buildPdfOptions: actual.buildPdfOptions,
|
||||
initBrowser: vi.fn().mockResolvedValue(undefined),
|
||||
closeBrowser: vi.fn().mockResolvedValue(undefined),
|
||||
renderPdf: vi.fn().mockResolvedValue({ pdf: Buffer.from("%PDF-1.4 mock pdf content here"), durationMs: 10 }),
|
||||
|
|
@ -68,7 +71,8 @@ vi.mock("../services/browser.js", () => ({
|
|||
uptimeMs: 10000,
|
||||
browsers: [],
|
||||
}),
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
// Mock verification service
|
||||
vi.mock("../services/verification.js", () => ({
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ export async function closeBrowser(): Promise<void> {
|
|||
instances.length = 0;
|
||||
}
|
||||
|
||||
import type { PaperFormat, PuppeteerLifeCycleEvent } from "puppeteer";
|
||||
import type { PaperFormat, PDFOptions, PuppeteerLifeCycleEvent } from "puppeteer";
|
||||
|
||||
export interface PdfRenderOptions {
|
||||
format?: PaperFormat;
|
||||
|
|
@ -238,6 +238,27 @@ export interface PdfRenderOptions {
|
|||
height?: string;
|
||||
}
|
||||
|
||||
/** Build a Puppeteer-compatible PDFOptions object from user-supplied render options. */
|
||||
export function buildPdfOptions(options: PdfRenderOptions): Record<string, unknown> {
|
||||
const result: Record<string, unknown> = {
|
||||
format: options.format || "A4",
|
||||
landscape: options.landscape || false,
|
||||
printBackground: options.printBackground !== false,
|
||||
margin: options.margin || { top: "0", right: "0", bottom: "0", left: "0" },
|
||||
};
|
||||
|
||||
if (options.headerTemplate !== undefined) result.headerTemplate = options.headerTemplate;
|
||||
if (options.footerTemplate !== undefined) result.footerTemplate = options.footerTemplate;
|
||||
if (options.displayHeaderFooter !== undefined) result.displayHeaderFooter = options.displayHeaderFooter;
|
||||
if (options.scale !== undefined) result.scale = options.scale;
|
||||
if (options.pageRanges) result.pageRanges = options.pageRanges;
|
||||
if (options.preferCSSPageSize !== undefined) result.preferCSSPageSize = options.preferCSSPageSize;
|
||||
if (options.width) result.width = options.width;
|
||||
if (options.height) result.height = options.height;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function renderPdf(
|
||||
html: string,
|
||||
options: PdfRenderOptions = {}
|
||||
|
|
@ -251,20 +272,7 @@ export async function renderPdf(
|
|||
(async () => {
|
||||
await page.setContent(html, { waitUntil: "domcontentloaded", timeout: 15_000 });
|
||||
await page.addStyleTag({ content: "* { margin: 0; padding: 0; } body { margin: 0; }" });
|
||||
const pdf = await page.pdf({
|
||||
format: options.format || "A4",
|
||||
landscape: options.landscape || false,
|
||||
printBackground: options.printBackground !== false,
|
||||
margin: options.margin || { top: "0", right: "0", bottom: "0", left: "0" },
|
||||
headerTemplate: options.headerTemplate,
|
||||
footerTemplate: options.footerTemplate,
|
||||
displayHeaderFooter: options.displayHeaderFooter || false,
|
||||
...(options.scale !== undefined && { scale: options.scale }),
|
||||
...(options.pageRanges && { pageRanges: options.pageRanges }),
|
||||
...(options.preferCSSPageSize !== undefined && { preferCSSPageSize: options.preferCSSPageSize }),
|
||||
...(options.width && { width: options.width }),
|
||||
...(options.height && { height: options.height }),
|
||||
});
|
||||
const pdf = await page.pdf(buildPdfOptions(options) as Parameters<Page["pdf"]>[0]);
|
||||
return Buffer.from(pdf);
|
||||
})(),
|
||||
new Promise<never>((_, reject) => {
|
||||
|
|
@ -330,20 +338,7 @@ export async function renderUrlPdf(
|
|||
waitUntil: options.waitUntil || "domcontentloaded",
|
||||
timeout: 30_000,
|
||||
});
|
||||
const pdf = await page.pdf({
|
||||
format: options.format || "A4",
|
||||
landscape: options.landscape || false,
|
||||
printBackground: options.printBackground !== false,
|
||||
margin: options.margin || { top: "0", right: "0", bottom: "0", left: "0" },
|
||||
...(options.headerTemplate && { headerTemplate: options.headerTemplate }),
|
||||
...(options.footerTemplate && { footerTemplate: options.footerTemplate }),
|
||||
...(options.displayHeaderFooter !== undefined && { displayHeaderFooter: options.displayHeaderFooter }),
|
||||
...(options.scale !== undefined && { scale: options.scale }),
|
||||
...(options.pageRanges && { pageRanges: options.pageRanges }),
|
||||
...(options.preferCSSPageSize !== undefined && { preferCSSPageSize: options.preferCSSPageSize }),
|
||||
...(options.width && { width: options.width }),
|
||||
...(options.height && { height: options.height }),
|
||||
});
|
||||
const pdf = await page.pdf(buildPdfOptions(options) as Parameters<Page["pdf"]>[0]);
|
||||
return Buffer.from(pdf);
|
||||
})(),
|
||||
new Promise<never>((_, reject) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue