diff --git a/src/__tests__/pdf-options.test.ts b/src/__tests__/pdf-options.test.ts index 224886f..99347fe 100644 --- a/src/__tests__/pdf-options.test.ts +++ b/src/__tests__/pdf-options.test.ts @@ -159,4 +159,117 @@ describe("validatePdfOptions", () => { expect(validatePdfOptions({ pageRanges: "all" }).valid).toBe(false); }); }); + + // --- waitUntil --- + describe("waitUntil", () => { + const validValues = ["load", "domcontentloaded", "networkidle0", "networkidle2"]; + for (const value of validValues) { + it(`accepts "${value}"`, () => { + const result = validatePdfOptions({ waitUntil: value }); + expect(result.valid).toBe(true); + if (result.valid) { + expect(result.sanitized.waitUntil).toBe(value); + } + }); + } + + it("rejects invalid string", () => { + const result = validatePdfOptions({ waitUntil: "invalid" }); + expect(result.valid).toBe(false); + if (!result.valid) { + expect(result.error).toContain("waitUntil"); + expect(result.error).toContain("load"); + expect(result.error).toContain("domcontentloaded"); + expect(result.error).toContain("networkidle0"); + expect(result.error).toContain("networkidle2"); + } + }); + + it("rejects number", () => { + const result = validatePdfOptions({ waitUntil: 123 as any }); + expect(result.valid).toBe(false); + if (!result.valid) { + expect(result.error).toContain("waitUntil"); + } + }); + + it("rejects boolean", () => { + const result = validatePdfOptions({ waitUntil: true as any }); + expect(result.valid).toBe(false); + }); + }); + + // --- headerTemplate --- + describe("headerTemplate", () => { + it("accepts string under size limit", () => { + const template = "
Header"; + const result = validatePdfOptions({ headerTemplate: template }); + expect(result.valid).toBe(true); + if (result.valid) { + expect(result.sanitized.headerTemplate).toBe(template); + } + }); + + it("accepts exactly 100KB", () => { + const template = "a".repeat(102400); // exactly 100KB + const result = validatePdfOptions({ headerTemplate: template }); + expect(result.valid).toBe(true); + }); + + it("rejects over 100KB", () => { + const template = "a".repeat(102401); // 100KB + 1 char + const result = validatePdfOptions({ headerTemplate: template }); + expect(result.valid).toBe(false); + if (!result.valid) { + expect(result.error).toContain("headerTemplate"); + expect(result.error).toContain("100KB"); + } + }); + + it("rejects non-string", () => { + const result = validatePdfOptions({ headerTemplate: 123 as any }); + expect(result.valid).toBe(false); + if (!result.valid) { + expect(result.error).toContain("headerTemplate"); + expect(result.error).toContain("string"); + } + }); + }); + + // --- footerTemplate --- + describe("footerTemplate", () => { + it("accepts string under size limit", () => { + const template = "Footer"; + const result = validatePdfOptions({ footerTemplate: template }); + expect(result.valid).toBe(true); + if (result.valid) { + expect(result.sanitized.footerTemplate).toBe(template); + } + }); + + it("accepts exactly 100KB", () => { + const template = "a".repeat(102400); // exactly 100KB + const result = validatePdfOptions({ footerTemplate: template }); + expect(result.valid).toBe(true); + }); + + it("rejects over 100KB", () => { + const template = "a".repeat(102401); // 100KB + 1 char + const result = validatePdfOptions({ footerTemplate: template }); + expect(result.valid).toBe(false); + if (!result.valid) { + expect(result.error).toContain("footerTemplate"); + expect(result.error).toContain("100KB"); + } + }); + + it("rejects non-string", () => { + const result = validatePdfOptions({ footerTemplate: 123 as any }); + expect(result.valid).toBe(false); + if (!result.valid) { + expect(result.error).toContain("footerTemplate"); + expect(result.error).toContain("string"); + } + }); + }); }); diff --git a/src/utils/pdf-options.ts b/src/utils/pdf-options.ts index ec7fd05..4442fa2 100644 --- a/src/utils/pdf-options.ts +++ b/src/utils/pdf-options.ts @@ -2,6 +2,8 @@ const VALID_FORMATS = ["Letter", "Legal", "Tabloid", "Ledger", "A0", "A1", "A2", const FORMAT_MAP = new Map(VALID_FORMATS.map(f => [f.toLowerCase(), f])); const PAGE_RANGES_RE = /^\d+(-\d*)?(\s*,\s*\d+(-\d*)?)*$/; const MARGIN_KEYS = new Set(["top", "right", "bottom", "left"]); +const VALID_WAIT_UNTIL = ["load", "domcontentloaded", "networkidle0", "networkidle2"]; +const MAX_TEMPLATE_SIZE = 102400; // 100KB in characters type PdfInput = Record