eCourt-Doc-Edit/docs/acceptance-review.md
Suphonchai Phoonsawat 9bb4a48c00 Phase 10: field editor (FR-14)
เพิ่ม editor ต่อ template field — ผู้ดูแลเลือกรูปแบบช่องกรอกในฟอร์มสร้างเอกสารได้:
text → textbox/textarea, number/date → control ตาม type (presentation metadata
ล้วน — validation ยังยึด Type, ค่า field เป็น string ทั้ง flow, ไม่แตะ generate/sync/add-in)

- Backend: TemplateField.Editor (nullable, null = default ของ type) +
  migration AddTemplateFieldEditor + validate ตาม type ใน PUT templates
  (ค่าไม่รู้จัก/ไม่เข้ากับ type → 400, normalize-on-write, API คืน resolved เสมอ)
- Web: util fieldEditor.ts + editor select หน้าแก้ template (desktop+mobile,
  ซ่อนเมื่อ type มีทางเดียว) + ฟอร์มสร้างเอกสาร render ตาม editor +
  แก้ prefill re-generate ของ field date จาก ISO timestamp เต็ม (input[type=date]
  รับเฉพาะ yyyy-MM-dd เดิมแสดงว่าง)
- Docs: spec v6 (FR-14/AC-17) + plan Phase 10 + sub-plan field-editor.md +
  acceptance-review AC-17 + README/CLAUDE.md
- Tests: backend 60/60 (FieldEditorTests ใหม่ 8 กรณี) · web 46/46 + build ผ่าน

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-22 09:17:15 +07:00

105 lines
16 KiB
Markdown
Raw 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.

# ตรวจ Acceptance Criteria 1–17 (Phase 8 + 9 + 10)
> วันที่ตรวจ: 2026-08-20 (AC 1–16) · 2026-08-22 (AC-17) · อ้างอิง AC จาก [`specs/spec-officejs.md`](../specs/spec-officejs.md) ส่วน 10
>
> สัญลักษณ์: ✅ ผ่าน (มี automated test คุ้ม) · 🖐️ ผ่านจากการทดสอบด้วยมือ · 🔶 ผ่านบางส่วน/รอยืนยันด้วยมือบน platform ที่เหลือ · ❌ ไม่ผ่าน
สรุปรวม: **AC ทั้ง 17 ข้อผ่านในระดับที่ตรวจอัตโนมัติได้ครบ** (backend ครอบทุก flow ด้วย integration tests บน PostgreSQL + MinIO + Keycloak จริงผ่าน Testcontainers) — ส่วนที่เหลือคือการยืนยันด้วยมือบน browser/Word จริง (โดยเฉพาะ Word Online + Windows) ตามรายการท้ายเอกสาร
## ผลทีละข้อ
### AC-1 — Sideload บน Word Desktop และ Word Online ได้ทั้งคู่ ไม่มี error ตอนโหลด task pane — 🔶
- 🖐️ **Word for Mac (Desktop): ผ่าน** — sideload ผ่าน `npm start` (manifest symlink) ตรวจตอน Phase 5 พร้อมแก้ปัญหาตกมุม 4 รอบ (dialog https / slice format / mixed content / blob download)
- ✅ อัตโนมัติ: `npm run build` + `npm run lint` + `npm run validate` (manifest) ผ่านทุกครั้ง
- 🔶 **Word Online + Word for Windows: ยังไม่ได้ทดสอบ** (ต้องมีเครื่อง/บัญชีทดสอบ)
### AC-2 — เปิดครั้งแรกยังไม่ login → Sign in → dialog → login สำเร็จ dialog ปิดเอง ได้ token — ✅ (automated) + 🖐️ (Mac)
- ✅ `authService.test.ts` (14 tests): PKCE pair format, state validation, token exchange, refresh, dialog message flow
- 🖐️ ทดสอบจริงบน Word for Mac ผ่าน (Keycloak https:8443 + Office Dialog API)
### AC-3 — เรียก backend โดยไม่แนบ token → 401 — ✅
- `AuthTests` (xUnit + Keycloak จริง): ครบทุก endpoint ทั้ง GET/POST + กรณี token ขยะ + กรณี token จริงได้ 200
### AC-4 — ไฟล์ใหม่ไม่เคย track → ปุ่ม "Start Versioning This Document" — ✅
- `metadataService.test.ts`: ตรวจ `SavedByAddIn` เทียบ ADDIN_SIGNATURE_GUID → state `untracked` (App state machine)
- 🖐️ ยืนยันแล้วบน Word for Mac ตอน Phase 5
### AC-5 — Start Versioning → stamp metadata, สร้าง Document, v1, `CreatedByUserId` ตรง `sub` ใน JWT — ✅
- `UploadVersionTests.FirstUpload_CreatesDocumentAndVersionRow_FromJwtClaims`: token จริงจาก Keycloak → ตรวจ row ใน Postgres ตรงๆ ว่า `CreatedByUserId == sub` + SHA256 + object key format ถูกต้อง
### AC-6 — ปิดแล้วเปิดไฟล์เดิม → ตรวจพบ tracked + โหลด history ถูก — ✅
- `UploadVersionTests.SecondUpload_...` + `GetVersions` ordering (ใหม่ → เก่า) + snapshot ต่อ version
- 🖐️ ยืนยันบน Mac แล้ว (round 3 ของ Phase 5)
### AC-7 — แก้ไขแล้ว Save & Version → version ใหม่ + `LastVersionId`/`LastVersionTimestamp` อัปเดต — ✅
- `versionFlow.test.ts` (7 ขั้นตาม spec ครบ) + metadataService stamp tests + `SecondUpload_IncrementsVersionNumber`
- หมายเหตุจาก Phase 5: `LastVersionId` ในไฟล์ = `clientVersionId` (ต้อง stamp ก่อน `document.save()`)
### AC-8 — ดาวน์โหลด version เก่าได้ไฟล์ที่เปิดได้ เนื้อหาตรงตอน upload — ✅
- `UploadVersionTests.Download_ReturnsExactUploadedBytes_WithDocxContentType`: byte-for-byte ผ่าน MinIO ไป-กลับ + content-type ถูก
- 🖐️ บน Mac ใช้ presigned link + `openBrowserWindow` (แก้จาก blob download ที่ WKWebView ทิ้งเงียบๆ) — ผ่านการตรวจ curl แล้ว ยืนยัน click จริงอีกครั้งพร้อม Word Online
### AC-9 — token หมดอายุกลาง session → silent refresh ผู้ใช้ไม่รู้สึก — ✅
- `apiClient.test.ts`: 401 → refresh → retry ครั้งเดียว → ยัง 401 → สัญญาณกลับหน้า sign-in · `useAuth`/`useApi` ของ web ใช้พฤติกรรมเดียวกัน
- 🔶 รอยืนยันด้วยมือ (ปล่อย session ทิ้งไว้จริง)
### AC-10 — XML Part fail silent บน Mac → ระบบยังทำงานถูก (backend = source of truth) — ✅
- `metadataService.test.ts`: XML write fail → catch ไม่ throw · ทุก history อ่านจาก backend เสมอ
### AC-11 — upload template → detect fields อัตโนมัติ + บันทึก definitions — ✅
- `TemplateTests`: SDT ครบทุกรูปแบบ (run เดี่ยว / multi-run คนละ formatting / block / tag ซ้ำ → dedup) + defaults (label=tag, text, required=false) + PUT บันทึกนิยาม + reject key นอกไฟล์/type ผิด
- 🖐️ หน้าเว็บอัปโหลด: ตรวจ SSR + จำลอง request sequence แล้ว (Phase 6) รอ click-through บน browser
### AC-12 — กรอก form บนเว็บ → docx ค่าครบ formatting ไม่พัง → เปิดใน Word เห็น tracked + v1 ทันที — ✅ (backend) + 🔶 (เต็มรูปแบบรอมือ)
- `GenerateTests.Generate_FillsAllFields_...`: ค่าครบทุก SDT + **multi-run รวมเป็น run เดียวโดยรักษา bold ของ run แรก** + custom properties (`SavedByAddIn` = GUID ตรงเป๊ะ, `DocumentTrackingId` = documentId) + Document/v1/field values ใน Postgres ครบ
- 🖐️ ส่วน "เปิดใน Word แล้ว add-in เห็น v1 ทันที": ตรวจด้วย unzip/OpenXML แล้ว ต้องยืนยัน click จริงบน Word
### AC-13 — แก้ค่าใน content control แล้ว Save & Version → field value ใน DB เปลี่ยน + snapshot ต่อ version — ✅
- `FieldSyncTests.UploadWithChangedFields_...`: latest values อัปเดตเฉพาะ field ที่เปลี่ยน (`SourceVersionId` ชี้ v2) + snapshot v1/v2 เก็บค่าต่างกันตามเวลา (diff ได้)
### AC-14 — ลบ content control แล้ว Save & Version → flow สำเร็จ ค่าเดิมคงอยู่ mark missing ไม่ error — ✅
- `FieldSyncTests.UploadWithMissingFields_...`: request 201 · field ที่หายคงค่า v1 + `SourceVersionId` ไม่ขยับ · snapshot `missing:true` พร้อมค่าเดิม · field เกินนิยามถูก ignore ไม่เก็บ
### AC-15 — หน้ารายการเอกสารของ template + detail (fields + history + snapshot ต่อ version + ดาวน์โหลด) + re-generate เป็น v1 โดยต้นทางไม่ถูกแตะ — ✅
- `GenerateTests.ListDocuments_ShowsVersionCounts_LatestFirst` (versionCount/Latest + เรียงล่าสุดก่อน) + `GetDetail_MergesDefsAndValues_BothOrphanWays` (merge นิยาม × ค่า รองรับ orphan สองทาง) + `GetDetail_OfStandaloneDocument`
- Re-generate: ผ่าน composition เดิม (detail → prefill → generate) — ตรวจ curl จริง Phase 6.4 ว่า **versionCount ของเอกสารต้นทางคงเดิม** + `TemplateTests.Deactivate_...` ยืนยัน policy deactivate
- 🖐️ หน้าเว็บ: SSR ทุก route 200 แล้ว รอ click-through
### AC-16 — (v5) พิมพ์คำอธิบาย (commit message) ตอนสร้าง version ได้ทุก flow → เก็บกับ version นั้น + แสดงใน history ทุกหน้า · เว้นว่างได้ — ✅ (backend + service) + 🔶 (dialog ใน Word รอมือ)
- ✅ `VersionMessageTests` (backend): upload พร้อม message → ตรวจ row ใน Postgres + echo ใน history · blank/whitespace → null · trim · oversize 2049 → 400 ไม่มี side effect · ยาวพอดี 2048 ผ่าน · ต่อ version มี message ของตัวเอง · generate v1 พร้อม message · generate oversize → 400 ก่อนแตะ MinIO
- ✅ `versionFlow.test.ts` (addin): `saveAndVersion`/`startVersioning` แนบ `message` ใน multipart · whitespace ล้วน → ไม่แนบฟิลด์
- ✅ Web: `VersionListItem.message` + build ผ่าน (type-only ตาม convention ไม่มี component test)
- 🔶 dialog + textarea ใน task pane (Save & Version / Start Versioning) และการแสดงใน `VersionHistoryPanel` ต้องยืนยันด้วยมือบน Word จริง
### AC-17 — (v6) admin เลือก editor ต่อ field ได้ (text → textbox/textarea) → ฟอร์มสร้างเอกสารใช้ control ตาม editor · number/date ใช้ control ตาม type · editor ผิด → 400 · ไม่ส่ง → default — ✅ (backend + web unit) + 🔶 (browser รอมือ)
- ✅ `FieldEditorTests` (backend): PUT editor=textarea → persist + echo · ไม่ส่ง → resolve default ตาม type + DB เก็บ null · ส่งค่า = default → normalize เป็น null · editor ไม่รู้จัก → 400 · ไม่เข้ากับ type (3 คู่) → 400 · tag ใหม่จาก replace ได้ default
- ✅ `fieldEditor.spec.ts` (web): `defaultEditor`/`resolveEditor`/`editorOptions`/`dateInputValue` (prefill re-generate ของ field date จาก timestamp เต็ม)
- 🔶 Click-through บน browser: admin editor select (desktop + mobile) · ฟอร์มสร้างเอกสาร render textarea/number/date ตาม editor
## ชุด test ที่คุ้ม AC (สรุป)
| ชุด | จำนวน | ครอบ |
|---|---|---|
| backend integration (Testcontainers: Postgres + MinIO + Keycloak จริง) | 60 | AC-3, 5, 6, 7 (backend), 8, 11–17 |
| addin unit (Jest) | 50 | AC-2, 4, 7 (client flow), 9, 10, 16 (ส่วน form) |
| web unit (Vitest) | 46 | AC-11/12 (validation ฝั่งฟอร์ม), theme, snapshot rendering, AC-17 (ส่วน editor util) |
## ปัญหาที่เจอระหว่างไล่ AC (และการแก้)
1. **Keycloak ใน Testcontainers พร้อมช้ากว่า port เปิด** — docker-proxy รับ connection ก่อนแล้วตัดสาย ("response ended prematurely") → fixture poll discovery endpoint จนได้ 200 จริง + แนบ container logs ตอน timeout
2. **`WithResourceMapping` ของ Testcontainers 4.14 รับ target เป็น directory** — ชี้ path ไฟล์ตรงๆ ทำให้ realm ไม่ถูก import (discovery 404 ตลอด 2 นาที) → ชี้ `/opt/keycloak/data/import` แล้วไฟล์เข้าไปชื่อเดิม
3. **Keycloak 26 บังคับ HTTPS บน token endpoint ของ master realm แม้ใน container http** → เลิกใช้ master admin API สร้าง test user ตามแผน ใช้ `testuser` ที่มากับ realm import + password grant แทน (backend ยัง validate JWKS/issuer/audience เต็มขั้น)
4. **template .docx ที่ test สร้างเองผิด schema** — วาง run-level SDT เป็นลูกตรงของ Body (OOXML ห้าม; Word วางไว้ใน paragraph เสมอ) ตอน reload กลายเป็น SdtBlock + unknown elements ทั้งชุด ทำให้ generate เข้า branch ผิด → แก้ `TestDocx` ให้ถูก schema (service ฝั่ง backend ถูกอยู่แล้ว — ยืนยันด้วย template จริงตั้งแต่ Phase 3)
5. **`import.meta.client` ของ Nuxt ไม่ถูก replace ใน Vitest** (vite-node ไม่ apply define กับ import.meta.*) → `useTheme` เปลี่ยน guard เป็น `typeof window !== 'undefined'` (semantics เทียบเท่าใน SSR/client)
6. **ยืนยัน behavior ผ่าน test (ไม่ใช่ bug):** field sync update latest value ของทุก field ที่ "ส่งมา" แม้ค่าไม่เปลี่ยน (`SourceVersionId` ขยับทุกตัวที่ส่ง) — เฉพาะ field ที่ "ไม่ได้ส่ง" เท่านั้นที่คงค่า+source เดิม ตาม policy FR-11
7. (ย้อนอดีต — จาก Phase 5) Office Dialog API ปฏิเสธ http, slice.data หลายรูปแบบ, WKWebView บล็อก mixed content + blob download — แก้ครบแล้ว มี regression coverage ใน unit tests
## ที่เหลือ (ต้องทำด้วยมือ — ทำแทนไม่ได้)
- [ ] Word Online: sideload + login + full flow (AC-1, 2, 4–8 บน Online)
- [ ] Word for Windows (ถ้ามีเครื่อง)
- [ ] ปล่อย session จริงจน token หมดอายุ → ยืนยัน silent refresh (AC-9)
- [ ] Click-through บน browser: สร้าง/แก้ template → สร้างเอกสาร → ดาวน์โหลด → เปิดใน Word ยืนยัน add-in เห็น v1 ทันที (AC-11, 12) + ดาวน์โหลด version ผ่าน presigned link บน Mac อีกครั้ง (AC-8)
- [ ] Theme toggle จำค่าหลัง reload บน browser จริง (Phase 6 DoD ที่เหลือ)
- [ ] (v5) ใน Word จริง: textarea ใน dialog ของทั้ง Save & Version และ Start Versioning + ข้อความโชว์ใน `VersionHistoryPanel` (AC-16) · และบน browser: ฟอร์มสร้างเอกสารพร้อม message + แสดงใน history ทั้ง desktop/mobille
- [ ] (v6) บน browser: หน้าแก้ template เลือก editor `textarea` (desktop + mobile) · ฟอร์มสร้างเอกสาร render textarea/ช่องตัวเลข/date picker ตาม editor · re-generate เอกสารที่ field date เก็บ timestamp เต็มแล้วช่อง date แสดงวันที่ถูกต้อง (AC-17)