Skip to content

Commit 7df87f4

Browse files
committed
fix: add validation, make layout optional, and add tests for HTML layout and watermark fields
1 parent 9b893b2 commit 7df87f4

2 files changed

Lines changed: 125 additions & 9 deletions

File tree

src/schemas.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,18 +192,18 @@ export const HTMLLayoutSchema = z
192192
.union([
193193
PageSizePresetSchema,
194194
z.object({
195-
width: z.number().describe('Page width in millimeters.'),
196-
height: z.number().describe('Page height in millimeters.'),
195+
width: z.number().positive().describe('Page width in millimeters.'),
196+
height: z.number().positive().describe('Page height in millimeters.'),
197197
}),
198198
])
199199
.optional()
200200
.describe('Page size as a preset name (e.g. "A4", "Letter") or custom dimensions in millimeters.'),
201201
margin: z
202202
.object({
203-
left: z.number().describe('Left margin in millimeters.'),
204-
top: z.number().describe('Top margin in millimeters.'),
205-
right: z.number().describe('Right margin in millimeters.'),
206-
bottom: z.number().describe('Bottom margin in millimeters.'),
203+
left: z.number().min(0).describe('Left margin in millimeters.'),
204+
top: z.number().min(0).describe('Top margin in millimeters.'),
205+
right: z.number().min(0).describe('Right margin in millimeters.'),
206+
bottom: z.number().min(0).describe('Bottom margin in millimeters.'),
207207
})
208208
.optional()
209209
.describe('Page margins in millimeters.'),
@@ -222,7 +222,7 @@ export const FilePartSchema = z.object({
222222
.string()
223223
.optional()
224224
.describe("Used to determine the file type when the file content type is not available and can't be inferred."),
225-
layout: HTMLLayoutSchema.describe(
225+
layout: HTMLLayoutSchema.optional().describe(
226226
'Page layout options for HTML-to-PDF conversion. Only applies when the input is an HTML file. ' +
227227
'Supports orientation, page size, and margins.',
228228
),
@@ -305,7 +305,7 @@ export const BaseWatermarkPropertiesSchema = z.object({
305305
bottom: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the bottom edge of a page.'),
306306
left: WatermarkDimensionSchema.optional().describe('Offset of the watermark from the left edge of a page.'),
307307
fontFamily: z.string().optional().describe('The font family to use for text watermarks.'),
308-
fontSize: z.number().optional().describe('Font size in points for text watermarks.'),
308+
fontSize: z.number().positive().optional().describe('Font size in points for text watermarks.'),
309309
fontStyle: z
310310
.array(z.enum(['bold', 'italic']))
311311
.optional()

tests/unit.test.ts

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
22
import fs, { Stats } from 'fs'
33
import { Readable } from 'stream'
4-
import { AiRedactArgsSchema, Instructions, SignatureOptions } from '../src/schemas.js'
4+
import type { Instructions, SignatureOptions } from '../src/schemas.js'
5+
import { AiRedactArgsSchema, BaseWatermarkPropertiesSchema, FilePartSchema } from '../src/schemas.js'
56
import { config as dotenvConfig } from 'dotenv'
67
import { performBuildCall } from '../src/dws/build.js'
78
import { performSignCall } from '../src/dws/sign.js'
@@ -40,6 +41,121 @@ function createMockStream(content: string | Buffer): Readable {
4041
return readable
4142
}
4243

44+
describe('Schema validation', () => {
45+
describe('HTMLLayoutSchema / FilePartSchema', () => {
46+
it('accepts a valid layout with orientation, size, and margin', () => {
47+
const result = FilePartSchema.safeParse({
48+
file: '/test.html',
49+
layout: {
50+
orientation: 'portrait',
51+
size: {
52+
width: 210,
53+
height: 297,
54+
},
55+
margin: {
56+
left: 10,
57+
top: 10,
58+
right: 10,
59+
bottom: 10,
60+
},
61+
},
62+
})
63+
64+
expect(result.success).toBe(true)
65+
})
66+
67+
it('allows omitting layout entirely', () => {
68+
const result = FilePartSchema.safeParse({ file: '/test.html' })
69+
expect(result.success).toBe(true)
70+
})
71+
72+
it('rejects negative margins', () => {
73+
const result = FilePartSchema.safeParse({
74+
file: '/test.html',
75+
layout: {
76+
margin: {
77+
left: -1,
78+
top: 0,
79+
right: 0,
80+
bottom: 0,
81+
},
82+
},
83+
})
84+
85+
expect(result.success).toBe(false)
86+
})
87+
88+
it('rejects zero or negative custom page sizes', () => {
89+
const zeroWidth = FilePartSchema.safeParse({
90+
file: '/test.html',
91+
layout: {
92+
size: {
93+
width: 0,
94+
height: 100,
95+
},
96+
},
97+
})
98+
99+
const negativeHeight = FilePartSchema.safeParse({
100+
file: '/test.html',
101+
layout: {
102+
size: {
103+
width: 100,
104+
height: -1,
105+
},
106+
},
107+
})
108+
109+
expect(zeroWidth.success).toBe(false)
110+
expect(negativeHeight.success).toBe(false)
111+
})
112+
})
113+
114+
describe('Watermark positioning and font fields', () => {
115+
it('accepts valid positioning and font fields', () => {
116+
const result = BaseWatermarkPropertiesSchema.safeParse({
117+
type: 'watermark',
118+
watermarkType: 'text',
119+
width: 100,
120+
height: 50,
121+
top: 10,
122+
right: '5%',
123+
bottom: 12,
124+
left: 8,
125+
text: 'DRAFT',
126+
fontFamily: 'Helvetica',
127+
fontSize: 12,
128+
fontStyle: ['bold', 'italic'],
129+
})
130+
131+
expect(result.success).toBe(true)
132+
})
133+
134+
it('rejects invalid fontSize values (0 or negative)', () => {
135+
const zero = BaseWatermarkPropertiesSchema.safeParse({
136+
type: 'watermark',
137+
watermarkType: 'text',
138+
width: 100,
139+
height: 50,
140+
text: 'DRAFT',
141+
fontSize: 0,
142+
})
143+
144+
const negative = BaseWatermarkPropertiesSchema.safeParse({
145+
type: 'watermark',
146+
watermarkType: 'text',
147+
width: 100,
148+
height: 50,
149+
text: 'DRAFT',
150+
fontSize: -1,
151+
})
152+
153+
expect(zero.success).toBe(false)
154+
expect(negative.success).toBe(false)
155+
})
156+
})
157+
})
158+
43159
describe('API Functions', () => {
44160
const originalEnv = process.env
45161

0 commit comments

Comments
 (0)