import { describe, it, expect, vi, beforeEach } from "vitest"; import express from "express"; import request from "supertest"; vi.mock("../services/browser.js", () => ({ renderPdf: vi.fn(), renderUrlPdf: vi.fn(), })); let app: express.Express; beforeEach(async () => { vi.clearAllMocks(); vi.resetModules(); const { renderPdf } = await import("../services/browser.js"); vi.mocked(renderPdf).mockResolvedValue({ pdf: Buffer.from("%PDF-1.4 mock"), durationMs: 10 }); const { demoRouter } = await import("../routes/demo.js"); app = express(); app.use(express.json({ limit: "500kb" })); app.use("/v1/demo", demoRouter); }); describe("Demo Branch Coverage", () => { describe("injectWatermark fallback branch (line 19)", () => { it("should append watermark when full HTML document doesn't contain tag", async () => { const { renderPdf } = await import("../services/browser.js"); // Send full HTML (with ) but without to hit the fallback branch const htmlWithoutClosingBody = ` Test Page

Hello

Content here

`; const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: htmlWithoutClosingBody }); expect(res.status).toBe(200); expect(res.headers["content-type"]).toMatch(/application\/pdf/); // Verify watermark was appended (not replaced) const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; // The fallback should append the watermark at the end expect(calledHtml).toContain("Hello"); expect(calledHtml).toContain("Content here"); expect(calledHtml).toContain("DEMO"); expect(calledHtml).toContain("Generated by DocFast"); // Ensure the original HTML is preserved before the watermark expect(calledHtml.indexOf("Hello")).toBeLessThan(calledHtml.indexOf("DEMO")); // Ensure watermark is appended at the end (since there's no to replace) const lastBodyCloseIndex = calledHtml.lastIndexOf(""); const watermarkIndex = calledHtml.indexOf("Generated by DocFast"); // If there's a at the very end (from wrapping), the watermark should be before it if (lastBodyCloseIndex > -1) { expect(watermarkIndex).toBeLessThan(lastBodyCloseIndex); } }); it("should append watermark to plain HTML fragment without ", async () => { const { renderPdf } = await import("../services/browser.js"); const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: "
Simple fragment
" }); expect(res.status).toBe(200); const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; expect(calledHtml).toContain("
Simple fragment
"); expect(calledHtml).toContain("DEMO"); expect(calledHtml).toContain("position:fixed;bottom:0;left:0;right:0;"); }); it("should handle markdown that results in HTML without and injects watermark", async () => { const { renderPdf } = await import("../services/browser.js"); const res = await request(app) .post("/v1/demo/markdown") .set("content-type", "application/json") .send({ markdown: "# Just a heading\n\nSome text" }); expect(res.status).toBe(200); const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; // Should contain watermark expect(calledHtml).toContain("DEMO"); expect(calledHtml).toContain("Generated by DocFast"); expect(calledHtml).toContain("Upgrade to Pro for clean PDFs"); }); it("should still work correctly when HTML contains (replace branch)", async () => { const { renderPdf } = await import("../services/browser.js"); const fullHtml = ` Test

Complete HTML

With closing body tag

`; const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: fullHtml }); expect(res.status).toBe(200); const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; // When exists, watermark should be injected before it expect(calledHtml).toContain(""); expect(calledHtml).toContain("DEMO"); // The watermark should be between the content and closing const watermarkIndex = calledHtml.indexOf("Generated by DocFast"); const closingBodyIndex = calledHtml.indexOf(""); expect(watermarkIndex).toBeGreaterThan(-1); expect(closingBodyIndex).toBeGreaterThan(-1); expect(watermarkIndex).toBeLessThan(closingBodyIndex); }); it("should reject empty HTML input with 400 error", async () => { const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: "" }); // Empty HTML is rejected by validation expect(res.status).toBe(400); expect(res.body.error).toContain("html"); }); it("should handle HTML with multiple tags (uses first)", async () => { const { renderPdf } = await import("../services/browser.js"); const htmlWithMultipleBodies = ` First body Second body `; const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: htmlWithMultipleBodies }); expect(res.status).toBe(200); const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; // replace only replaces the first occurrence expect(calledHtml).toContain("First body"); expect(calledHtml).toContain("DEMO"); expect(calledHtml).toContain(""); }); }); describe("Watermark content verification", () => { it("should include demo watermark with exact styling", async () => { const { renderPdf } = await import("../services/browser.js"); const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: "

Test

" }); expect(res.status).toBe(200); const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; // Verify watermark styling expect(calledHtml).toContain("background:rgba(52,211,153,0.92);color:#0b0d11"); expect(calledHtml).toContain("z-index:999999"); expect(calledHtml).toContain("pointer-events:none"); }); it("should preserve user CSS when injecting watermark", async () => { const { renderPdf } = await import("../services/browser.js"); const customCss = "body { background: blue; }"; const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: "

Test

", css: customCss }); expect(res.status).toBe(200); const calledHtml = vi.mocked(renderPdf).mock.calls[0][0]; // Both watermark and user CSS should be present expect(calledHtml).toContain("DEMO"); expect(calledHtml).toContain("background: blue"); }); }); describe("Branch coverage for attachment headers", () => { it("should set Content-Disposition to attachment for HTML", async () => { const res = await request(app) .post("/v1/demo/html") .set("content-type", "application/json") .send({ html: "

Hello

" }); expect(res.status).toBe(200); expect(res.headers["content-disposition"]).toMatch(/^attachment/); expect(res.headers["content-disposition"]).toMatch(/filename="demo\.pdf"/); }); it("should set Content-Disposition to attachment for markdown", async () => { const res = await request(app) .post("/v1/demo/markdown") .set("content-type", "application/json") .send({ markdown: "# Hello" }); expect(res.status).toBe(200); expect(res.headers["content-disposition"]).toMatch(/^attachment/); expect(res.headers["content-disposition"]).toMatch(/filename="demo\.pdf"/); }); }); });