Bundles the prior autonomous-session output that was sitting unstaged: - Em-dash sweep across src/ + tests/ (en-dash/em-dash to hyphen, ~2280 instances) - country-flag-icons rollout (CountryFlag component, replaces emoji glyphs that never rendered on Windows; lazy-loads the 3x2 SVG index as a single chunk after the per-subpath dynamic-import approach silently failed in webpack) - Admin IA Phase 1+2: 7-domain regroup, 41 to 38 pages, /admin/berths index, redirects (ocr to ai, reports to dashboard, invitations to users), docs/admin-ia-proposal.md - Per-template email tester (registry + endpoint + UI on Email admin page) - Cancel-document mode picker (delete-from-Documenso vs keep-for-audit) - Dashboard PDF report: 25 widgets, SVG charts, date-range picker, 11 resolvers - Customize-widgets per-region sortables at xl+ (charts/rails/feed); single flat sortable below xl when the layout stacks; per-viewport saved orders - Audit doc updates capturing each shipped item - Lint fixes: react-compiler immutability in DonutChart (reduce instead of let-reassign), set-state-in-effect disables in CountryFlag and UploadForSigning preview-bytes effect, unused 'confirm' destructures in interest contract + reservation tabs, unescaped apostrophe in test-template card copy
60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
/**
|
|
* AcroForm-tier test for parseBerthPdf. Builds a synthetic PDF with named
|
|
* AcroForm fields via pdf-lib and asserts the parser pulls them out without
|
|
* needing OCR.
|
|
*/
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import { PDFDocument } from 'pdf-lib';
|
|
|
|
import { parseBerthPdf } from '@/lib/services/berth-pdf-parser';
|
|
|
|
async function buildAcroFormPdf(): Promise<Buffer> {
|
|
const doc = await PDFDocument.create();
|
|
doc.addPage([400, 400]);
|
|
const form = doc.getForm();
|
|
|
|
const fields: Array<[string, string]> = [
|
|
['mooring_number', 'A1'],
|
|
['length_ft', '206.67'],
|
|
['length_m', '63'],
|
|
['width_ft', '46.58'],
|
|
['width_m', '14.2'],
|
|
['power_capacity', '330'],
|
|
['voltage', '480'],
|
|
['weekly_rate_high_usd', '11341'],
|
|
['weekly_rate_low_usd', '8100'],
|
|
['daily_rate_high_usd', '1890'],
|
|
['daily_rate_low_usd', '1350'],
|
|
['pricing_valid_until', '2025-09-15'],
|
|
['bow_facing', 'East'],
|
|
['mooring_type', 'Side Pier / Med Mooring'],
|
|
];
|
|
for (const [name, value] of fields) {
|
|
const field = form.createTextField(name);
|
|
field.setText(value);
|
|
}
|
|
const bytes = await doc.save();
|
|
return Buffer.from(bytes);
|
|
}
|
|
|
|
describe('parseBerthPdf - AcroForm tier', () => {
|
|
it('extracts named fields and skips OCR', async () => {
|
|
const buf = await buildAcroFormPdf();
|
|
const result = await parseBerthPdf(buf, { skipOcr: true });
|
|
expect(result.engine).toBe('acroform');
|
|
expect(result.fields.mooringNumber?.value).toBe('A1');
|
|
expect(result.fields.lengthFt?.value).toBeCloseTo(206.67, 1);
|
|
expect(result.fields.lengthM?.value).toBe(63);
|
|
expect(result.fields.weeklyRateHighUsd?.value).toBe(11341);
|
|
expect(result.fields.pricingValidUntil?.value).toBe('2025-09-15');
|
|
expect(result.fields.bowFacing?.value).toBe('East');
|
|
expect(result.meanConfidence).toBe(1);
|
|
});
|
|
|
|
it('rejects a non-PDF buffer via magic-byte check', async () => {
|
|
await expect(parseBerthPdf(Buffer.from('not a pdf'))).rejects.toThrow(/magic-byte/);
|
|
});
|
|
});
|