/ tests / elements / page.test.ts
page.test.ts
  1  import { PageSizes } from "@cantoo/pdf-lib";
  2  import { describe, expect, mock, test } from "bun:test";
  3  import { div } from "~/elements/div";
  4  import { Page, page } from "~/elements/page";
  5  import { text } from "~/elements/text";
  6  
  7  // Mock PDFPage for testing
  8  const mockPDFFont = {
  9    heightAtSize: (size: number) => size,
 10    widthOfTextAtSize: (text: string, size: number) => text.length * size * 0.6
 11  };
 12  
 13  const mockPDFPage = {
 14    doc: {
 15      defaultWordBreaks: /\s/g
 16    },
 17    drawRectangle: mock(() => {}),
 18    drawText: mock(() => {}),
 19    getFont: mock(() => [mockPDFFont]),
 20    getHeight: mock(() => 841.8898), // A4 height
 21    getWidth: mock(() => 595.276), // A4 width
 22    setSize: mock(() => {})
 23  };
 24  
 25  describe("Page", () => {
 26    describe("constructor and helper function", () => {
 27      test("should create Page instance", () => {
 28        const pageElement = new Page();
 29        expect(pageElement).toBeInstanceOf(Page);
 30      });
 31  
 32      test("should create Page instance with helper function", () => {
 33        const pageElement = page();
 34        expect(pageElement).toBeInstanceOf(Page);
 35      });
 36    });
 37  
 38    describe("child management", () => {
 39      test("should add child and return instance for chaining", () => {
 40        const pageElement = page();
 41        const child = div();
 42  
 43        const result = pageElement.child(child);
 44  
 45        expect(result).toBe(pageElement);
 46      });
 47  
 48      test("should add multiple children", () => {
 49        const pageElement = page();
 50        const child1 = div();
 51        const child2 = text("Hello");
 52  
 53        pageElement.child(child1).child(child2);
 54  
 55        expect(pageElement).toBeInstanceOf(Page);
 56      });
 57    });
 58  
 59    describe("size methods", () => {
 60      test("should set width", () => {
 61        const pageElement = page();
 62        const result = pageElement.w(500);
 63  
 64        expect(result).toBe(pageElement);
 65      });
 66  
 67      test("should set height", () => {
 68        const pageElement = page();
 69        const result = pageElement.h(700);
 70  
 71        expect(result).toBe(pageElement);
 72      });
 73  
 74      test("should set size with array", () => {
 75        const pageElement = page();
 76        const result = pageElement.size([600, 800]);
 77  
 78        expect(result).toBe(pageElement);
 79      });
 80  
 81      test("should set size with PageSizes constant", () => {
 82        const pageElement = page();
 83        const result = pageElement.size(PageSizes.A4);
 84  
 85        expect(result).toBe(pageElement);
 86      });
 87  
 88      test("should set size with PageSizes Letter", () => {
 89        const pageElement = page();
 90        const result = pageElement.size(PageSizes.Letter);
 91  
 92        expect(result).toBe(pageElement);
 93      });
 94    });
 95  
 96    describe("render", () => {
 97      test("should call setSize on PDFPage", () => {
 98        const pageElement = page();
 99        const fonts = new Map();
100  
101        pageElement.render(mockPDFPage as any, fonts);
102  
103        expect(mockPDFPage.setSize).toHaveBeenCalled();
104      });
105  
106      test("should render with custom size", () => {
107        const pageElement = page().size([400, 600]);
108        const fonts = new Map();
109  
110        pageElement.render(mockPDFPage as any, fonts);
111  
112        expect(mockPDFPage.setSize).toHaveBeenCalledWith(400, 600);
113      });
114  
115      test("should render with children elements", () => {
116        const child1 = div().w(100).h(50).bg(0xFF0000);
117        const child2 = text("Hello World");
118  
119        const pageElement = page()
120          .child(child1)
121          .child(child2);
122  
123        const fonts = new Map();
124  
125        // Test that render doesn't throw with children
126        expect(() => {
127          pageElement.render(mockPDFPage as any, fonts);
128        }).not.toThrow();
129  
130        expect(mockPDFPage.setSize).toHaveBeenCalled();
131      });
132  
133      test("should handle empty page", () => {
134        const pageElement = page();
135        const fonts = new Map();
136  
137        expect(() => {
138          pageElement.render(mockPDFPage as any, fonts);
139        }).not.toThrow();
140  
141        expect(mockPDFPage.setSize).toHaveBeenCalled();
142      });
143    });
144  
145    describe("method chaining", () => {
146      test("should support complete method chaining", () => {
147        const child1 = div().w(100).h(100);
148        const child2 = text("Sample text");
149  
150        const pageElement = page()
151          .w(600)
152          .h(800)
153          .size([700, 900])
154          .child(child1)
155          .child(child2)
156          .w(500) // Should override previous width
157          .h(750); // Should override previous height
158  
159        expect(pageElement).toBeInstanceOf(Page);
160      });
161  
162      test("should chain multiple size operations", () => {
163        const pageElement = page()
164          .size(PageSizes.A4)
165          .w(400)
166          .h(600)
167          .size(PageSizes.Letter);
168  
169        expect(pageElement).toBeInstanceOf(Page);
170      });
171    });
172  
173    describe("default values", () => {
174      test("should use A4 size by default", () => {
175        const pageElement = page();
176        const fonts = new Map();
177  
178        pageElement.render(mockPDFPage as any, fonts);
179  
180        // A4 dimensions - using more lenient matching due to potential precision differences
181        const calls = (mockPDFPage.setSize as any).mock.calls;
182        const lastCall = calls[calls.length - 1];
183        expect(lastCall[0]).toBeCloseTo(595.276, 1);
184        expect(lastCall[1]).toBeCloseTo(841.8898, 1);
185      });
186    });
187  
188    describe("integration scenarios", () => {
189      test("should handle complex page layout", () => {
190        const header = div()
191          .w(500)
192          .h(50)
193          .bg(0x333333)
194          .child(text("Header"));
195  
196        const content = div()
197          .flex()
198          .flexCol()
199          .child(text("Content line 1"))
200          .child(text("Content line 2"));
201  
202        const footer = div()
203          .w(500)
204          .h(30)
205          .bg(0x666666)
206          .child(text("Footer"));
207  
208        const pageElement = page()
209          .size([500, 700])
210          .child(header)
211          .child(content)
212          .child(footer);
213  
214        expect(pageElement).toBeInstanceOf(Page);
215      });
216    });
217  });