eCourt-note/tests/scripts/patch-pdf-worker.test.ts
Your Name 8cc15ea75d
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 52s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
refactor(routes): move routes under /note, APIs under /api/note
เตรียม basePath /docedit เป็นพื้นที่กลางให้แอปพี่น้อง (wordaddin ใน
อนาคต) — เดิม basePath ครอบ /docedit/note ทั้งหมดจนแชร์ prefix ไม่ได้
(Next มี basePath ได้ทางเดียว) และจัด API แยกต้นไม้ตามที่ผู้ใช้ขอ

(1) โครงสร้างใหม่: basePath = /docedit + route หน้าทั้งหมดย้ายเข้า
app/note/** + API 4 เส้นย้ายเข้า app/api/note/** + statics ย้ายเข้า
public/note/** (samples, uploads, pdf.worker) — หน้า/statics URL ปลายทาง
คงเดิมเป๊ะ (/docedit/note/…) ส่วน URL API เปลี่ยนเป็น /docedit/api/note/…
ตามคำขอผู้ใช้ (N10 endpoint เปลี่ยน — ต้องแจ้ง backend e-Court ก่อนใช้)

(2) กลไก: withBasePath (utils/base-path.ts) เติม base + app path /note
ยกเว้น path ที่ขึ้นต้น /api/ เติมแค่ base (caller เขียน /api/note/… เต็ม);
resolvePublicPdfPath root ที่ public/note (traversal guard เดิม);
middleware matcher ยกเว้นใหม่ (note/login, api/note, note/uploads,
note/samples, note/pdf.worker, _next, fonts, favicon) + redirect
/note/login; next.config เพิ่ม redirects /docedit -> /docedit/note;
navigation ภายใน 19 จุด + revalidatePath 3 จุด ใส่ prefix /note;
pdf_url ใน DB เป็น logical path ไม่แก้แม้แต่ตัวเดียว (seed/upload
เขียนค่าเดิม, browser เติมผ่าน withBasePath, disk เติมผ่าน guard)

(3) ตามแก้: patch-pdf-worker (DEST public/note + mkdirSync ปลายทาง),
seed-samples (SAMPLES_DIR), uploadFile (UPLOAD_DIR), Dockerfile/
.dockerignore/.gitignore/docker-compose.test.yml, tests 2 ไฟล์ที่อ้าง
disk path จริง, sync เอกสารทั้งหมด (CLAUDE.md invariant #6/#12 + tree,
README, docs/API-DOCUMENTS.md, .claude/rules/*, sync-db skill)

(4) !! ตอน deploy ต้องย้าย volume ตาม: mount /app/public/uploads ->
/app/public/note/uploads + ย้ายไฟล์ใน volume ให้ตรงตำแหน่ง ไม่งั้นไฟล์
ที่เคยอัปโหลด (พร้อม annotation ที่ผูกอยู่) 404 ทันทีที่สลับ image

(5) verify: build ผ่าน (route table /api/note/** + /note/** ครบ), tsc,
vitest 215/215, curl ครบทุก endpoint (login/redirect chain, worker,
samples, uploads, SSE, api/pdf, export, N8 register, N10 bundle + deep
link), Playwright browser จริง (canvas + textLayer ไทย render, browser
ยิง URL ใหม่ทั้งหมดไม่มี request ไป URL เก่า), plan-reviewer +
code-reviewer 3 รายงาน 0 blocking

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-06 14:55:07 +07:00

69 lines
4.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { readFileSync } from "node:fs";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { describe, expect, it } from "vitest";
// patch fake-space ไทยของ pdf.js worker — scripts/patch-pdf-worker.mjs (bug 2026-09-24):
// pdf.js เก็บตำแหน่งอ้างอิง glyph ก่อนหน้า (prevTransform) เฉพาะ glyph ที่กว้าง ≠ 0
// ทำให้ charSpacing ของสระบน-ล่าง/วรรณยุกต์ (glyph กว้าง 0) สะสมเป็น gap จน
// addFakeSpaces แทรก " " กลางคำ ("ฉะนั้ น") — patch ถอด if ครอบการอัปเดตออก
// ให้อัปเดตทุก glyph แทน
const ROOT = process.cwd();
// statics ของแอปอยู่ใต้ public/note/** (URL /docedit/note/… — basePath /docedit + prefix /note)
const WORKER = join(ROOT, "public", "note", "pdf.worker.min.mjs");
// ไฟล์ Word→PDF ที่ justify ด้วย letter-spacing ทุก glyph (มี charSpacing ~0.05×fontSize)
// — เงื่อนไขที่ทำให้ heuristic ของ pdf.js แทรก fake space กลางคำไทย
// (อยู่ใน tests/fixtures/ ไม่ใช่ public/note/samples/ — ไม่ให้ seed-samples สร้าง
// รายการเอกสาร "ทดสอบ" บนหน้ารายการของทุก deploy)
const SAMPLE = join(ROOT, "tests", "fixtures", "thai_letterspacing.pdf");
describe("public/note/pdf.worker.min.mjs — ผ่าน patch fake-space ไทย", () => {
const code = readFileSync(WORKER, "utf8");
it("อัปเดต prevTransform ทุก glyph (รูปแบบก่อนแพตช์ C&&(h.prevTransform=…) หายไป)", () => {
// หลัง patch: }h.prevTransform=getCurrentTextTransform(); (เคยถูก if ครอบ
// จนเหลือ } ปิด block เปล่านำหน้า)
expect(code).toMatch(
/[;{}][A-Za-z$_][\w$]*\.prevTransform=getCurrentTextTransform\(\);/,
);
// ก่อน patch: C&&(h.prevTransform=getCurrentTextTransform());
expect(code).not.toMatch(
/&&\([A-Za-z$_][\w$]*\.prevTransform=getCurrentTextTransform\(\)\);/,
);
});
});
describe("getTextContent ผ่าน worker ที่ patch แล้ว — ไฟล์ justify letter-spacing", () => {
it("ไม่มี fake space กลางคำไทย (space ตามด้วยสระ/วรรณยุกต์ = ผิดเสมอ)", async () => {
const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
pdfjs.GlobalWorkerOptions.workerSrc = pathToFileURL(WORKER).href;
const doc = await pdfjs.getDocument({
data: new Uint8Array(readFileSync(SAMPLE)),
}).promise;
// space หน้า combining mark ไม่มีทางถูกต้องในภาษาไทย (mark ต้องเกาะฐานหน้า)
const badRe = / [ัิีึืุู็่้๊๋์ํ]/;
const offenders: string[] = [];
let targetLine = "";
for (let p = 1; p <= doc.numPages; p++) {
const tc = await (await doc.getPage(p)).getTextContent();
for (const item of tc.items) {
if (!("str" in item)) continue;
if (!targetLine && item.str.includes("ฉะนั้น")) targetLine = item.str;
if (badRe.test(item.str)) {
offenders.push(`p${p}: ${JSON.stringify(item.str.slice(0, 60))}`);
}
}
}
expect(offenders).toEqual([]);
// บรรทัดเป้าหมาย (เคยได้ "ฉะนั้ น ให้ ท่ าน…คำฟ้ อง") — space จริง (glyph SPACE
// ในไฟล์) คงอยู้ครบ ส่วนช่อง letter-spacing เล็ก ๆ ที่ไม่ใช่ space จริงติดกัน
// ("ให้การแก้" — ในไฟล์ไม่มี space glyph ตรงนั้น)
expect(targetLine).toBe(
"ฉะนั้น ให้ ท่านทำคำให้การแก้คำฟ้อง พร้อมด้วยพยานหลักฐาน ๑ ชุด",
);
});
});