test(05-01): add unit tests for Y-flip coordinate conversion formula
- Created src/lib/pdf/__tests__/prepare-document.test.ts with 10 test cases - Tests verify Y-axis flip: screenY=0 → pdfY≈792, screenY=792 → pdfY≈0 - Tests verify scale-invariance at both 1:1 and 50% zoom ratios - Installed jest, ts-jest, @types/jest and added jest config to package.json - All 10 tests pass Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Unit tests for the Y-flip coordinate conversion formula used in FieldPlacer.tsx.
|
||||
*
|
||||
* PDF user space: origin at bottom-left, Y increases upward.
|
||||
* DOM/screen space: origin at top-left, Y increases downward.
|
||||
* Formula must flip the Y axis.
|
||||
*
|
||||
* US Letter: 612 × 792 pts at 72 DPI.
|
||||
*/
|
||||
|
||||
// Pure conversion functions extracted from the FieldPlacer formula.
|
||||
// These MUST match what is implemented in FieldPlacer.tsx exactly.
|
||||
function screenToPdfY(screenY: number, renderedH: number, originalHeight: number): number {
|
||||
return ((renderedH - screenY) / renderedH) * originalHeight;
|
||||
}
|
||||
|
||||
function screenToPdfX(screenX: number, renderedW: number, originalWidth: number): number {
|
||||
return (screenX / renderedW) * originalWidth;
|
||||
}
|
||||
|
||||
const US_LETTER_W = 612; // pts
|
||||
const US_LETTER_H = 792; // pts
|
||||
|
||||
describe('Y-flip coordinate conversion (US Letter 612×792)', () => {
|
||||
describe('at 1:1 scale (rendered = original dimensions)', () => {
|
||||
const rW = US_LETTER_W;
|
||||
const rH = US_LETTER_H;
|
||||
|
||||
test('screenY=0 (visual top) produces pdfY≈792 (PDF top)', () => {
|
||||
expect(screenToPdfY(0, rH, US_LETTER_H)).toBeCloseTo(792, 1);
|
||||
});
|
||||
|
||||
test('screenY=792 (visual bottom) produces pdfY≈0 (PDF bottom)', () => {
|
||||
expect(screenToPdfY(792, rH, US_LETTER_H)).toBeCloseTo(0, 1);
|
||||
});
|
||||
|
||||
test('screenY=396 (visual center) produces pdfY≈396 (PDF center)', () => {
|
||||
expect(screenToPdfY(396, rH, US_LETTER_H)).toBeCloseTo(396, 1);
|
||||
});
|
||||
|
||||
test('screenX=0 produces pdfX=0', () => {
|
||||
expect(screenToPdfX(0, rW, US_LETTER_W)).toBeCloseTo(0, 1);
|
||||
});
|
||||
|
||||
test('screenX=612 (full width) produces pdfX≈612', () => {
|
||||
expect(screenToPdfX(612, rW, US_LETTER_W)).toBeCloseTo(612, 1);
|
||||
});
|
||||
|
||||
test('screenX=306 (horizontal center) produces pdfX≈306', () => {
|
||||
expect(screenToPdfX(306, rW, US_LETTER_W)).toBeCloseTo(306, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('at 50% zoom (rendered = half original dimensions)', () => {
|
||||
const rW = US_LETTER_W / 2; // 306
|
||||
const rH = US_LETTER_H / 2; // 396
|
||||
|
||||
test('screenY=0 at 50% zoom still produces pdfY≈792 (scale-invariant)', () => {
|
||||
expect(screenToPdfY(0, rH, US_LETTER_H)).toBeCloseTo(792, 1);
|
||||
});
|
||||
|
||||
test('screenY=396 (visual bottom at 50% zoom) produces pdfY≈0', () => {
|
||||
expect(screenToPdfY(396, rH, US_LETTER_H)).toBeCloseTo(0, 1);
|
||||
});
|
||||
|
||||
test('screenY=198 (visual center at 50% zoom) produces pdfY≈396', () => {
|
||||
expect(screenToPdfY(198, rH, US_LETTER_H)).toBeCloseTo(396, 1);
|
||||
});
|
||||
|
||||
test('screenX=153 (quarter width at 50% zoom) produces pdfX≈306 (half PDF width)', () => {
|
||||
expect(screenToPdfX(153, rW, US_LETTER_W)).toBeCloseTo(306, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user