เพิ่ม 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>
105 lines
16 KiB
Markdown
105 lines
16 KiB
Markdown
# ตรวจ 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)
|