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 });