@relipa/ai-flow-kit 0.2.1 → 0.2.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/custom/skills/create-system-requirement/SKILL.md +47 -9
- package/custom/skills/create-system-requirement/system-requirement-template-v1.md +220 -66
- package/custom/skills/execute-flow/SKILL.md +142 -36
- package/custom/skills/execute-flow/templates/evidence-helper.ts +145 -0
- package/custom/skills/execute-flow/templates/playwright.config.ts +28 -8
- package/custom/skills/read-study-requirement/SKILL.md +1 -2
- package/custom/skills/script-sync/SKILL.md +54 -16
- package/custom/templates/shared/gate-workflow.md +17 -11
- package/docs/common/CHANGELOG.md +8 -0
- package/docs/common/INDEX.md +1 -0
- package/docs/common/QUICK_START.md +1 -1
- package/docs/common/System-Requirement-Read-Guide.md +178 -0
- package/docs/common/Testing-Structure.md +31 -25
- package/docs/common/cli-reference.md +12 -10
- package/package.json +1 -1
- package/scripts/prompt.js +3 -3
- package/scripts/scaffold-playwright.js +2 -0
|
@@ -18,6 +18,40 @@ Flow độc lập để tester thực thi test từ TC file có sẵn, không c
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## Cấu trúc thư mục đầu ra
|
|
22
|
+
|
|
23
|
+
**Không còn dùng `ak-test/` working dir.** Toàn bộ output — script, evidence, result, report, bug — nằm trong `AK-Docs/03.Testing/`, theo cấu trúc chuẩn ở `docs/common/Testing-Structure.md`. `04.Evidence/` chứa screenshot/video/`trace.zip`/`results.json` thô, nhưng **phải nằm trong `.gitignore`** — đây vẫn là binary lớn, không commit lên Git kể cả khi nằm trong AK-Docs.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
AK-Docs/03.Testing/
|
|
27
|
+
├── 04.Evidence/{repo}/{featureDir}/run-{N}/ # ⚠️ gitignored — xem Gate 1 item 6
|
|
28
|
+
│ ├── results.json # Playwright JSON output thô
|
|
29
|
+
│ └── {TC_ID}-{kebab-scenario}/
|
|
30
|
+
│ ├── step-NN-{desc}.png # 1 ảnh / step có mô tả UI
|
|
31
|
+
│ └── trace.zip
|
|
32
|
+
├── 05.Scripts/ # Gate 2 — Playwright project, chạy được tại chỗ
|
|
33
|
+
│ ├── package.json / tsconfig.json / node_modules/ (gitignored)
|
|
34
|
+
│ ├── Shared/
|
|
35
|
+
│ │ ├── fixtures/test.ts
|
|
36
|
+
│ │ ├── BasePage.ts
|
|
37
|
+
│ │ └── evidence-helper.ts
|
|
38
|
+
│ └── {repo}/
|
|
39
|
+
│ ├── playwright.config.ts
|
|
40
|
+
│ ├── pages/{ScreenID}Page.ts
|
|
41
|
+
│ └── {featureDir}/{ScreenID}.spec.ts
|
|
42
|
+
├── 02.Reports/{repo}/{featureDir}/run-{N}/
|
|
43
|
+
│ ├── {TC_ID}-{kebab-scenario}/result.md
|
|
44
|
+
│ └── testreport.md # Gate 4
|
|
45
|
+
└── 06.Bugs/{repo}/{featureDir}/run-{N}/
|
|
46
|
+
└── BUG-{NNN}-{slug}.md # Gate 3
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`featureDir` = `{ScreenID}_{Screen-Name-kebab-case}` (vd `AD10_create-product`).
|
|
50
|
+
|
|
51
|
+
> **Lịch sử:** bản trước dùng `ak-test/` working dir ngoài AK-Docs cho evidence để tránh phình git history. Tester quyết định bỏ `ak-test/` hoàn toàn — evidence chuyển vào `04.Evidence/` (đúng theo mapping gốc của `docs/common/Testing-Structure.md`), và xử lý vấn đề binary lớn bằng `.gitignore` thay vì tách working dir riêng.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
21
55
|
## ⛩️ GATE 1 — Context & Work Plan
|
|
22
56
|
|
|
23
57
|
### Pre-flight checks (auto, không hỏi tester)
|
|
@@ -47,14 +81,44 @@ Kiểm tra trước khi làm gì:
|
|
|
47
81
|
> "Để cài thủ công: mở `~/.claude/settings.json`, thêm `playwright` vào `mcpServers`, rồi restart Claude Code."
|
|
48
82
|
|
|
49
83
|
1. TC file có parse được không? (file tồn tại, có Section 3 — Danh sách Test Cases)
|
|
50
|
-
2. `
|
|
84
|
+
2. `AK-Docs/03.Testing/` có tồn tại không? (thư mục chuẩn theo `docs/common/Testing-Structure.md` — nếu thiếu subfolder `04.Evidence/`, `05.Scripts/`, `02.Reports/`, `06.Bugs/` thì tự tạo)
|
|
51
85
|
3. `BASE_URL` env var có set không? Nếu không → hỏi: "BASE_URL chưa set. App đang chạy ở URL nào?"
|
|
52
86
|
|
|
53
|
-
Items 1–3 chạy ngay khi entry.
|
|
87
|
+
Items 1–3 chạy ngay khi entry. Items 4–6 chạy sau khi repo được xác định từ Bước 1 (parse TC file):
|
|
88
|
+
|
|
89
|
+
4. `AK-Docs/03.Testing/05.Scripts/{repo}/playwright.config.ts` có tồn tại không? Nếu không → **tự scaffold** (không cần hỏi tester), xem "Scaffold Playwright Project" bên dưới.
|
|
90
|
+
5. `AK-Docs/03.Testing/05.Scripts/package.json` + `node_modules/` có tồn tại không? Nếu chưa `npm install` → chạy `npm install` trong `AK-Docs/03.Testing/05.Scripts/`, sau đó `npx playwright install chromium` (một lần, cache browser ở `~/.cache/ms-playwright`, không nằm trong repo).
|
|
91
|
+
6. `.gitignore` (ở root của AK-Docs, hoặc root repo chứa AK-Docs) có ignore các dòng sau chưa? Nếu chưa → tự thêm (append, không xoá nội dung cũ):
|
|
92
|
+
- `03.Testing/04.Evidence/` — **bắt buộc**, evidence là binary (screenshot/video/trace.zip), không commit dù nằm trong AK-Docs
|
|
93
|
+
- `03.Testing/05.Scripts/**/node_modules/`, `03.Testing/05.Scripts/**/test-results/`, `03.Testing/05.Scripts/**/playwright-report/`, `03.Testing/05.Scripts/**/blob-report/`
|
|
94
|
+
|
|
95
|
+
Nếu bất kỳ check nào fail và không tự fix được → dừng, hướng dẫn fix. Không proceed.
|
|
96
|
+
|
|
97
|
+
### Scaffold Playwright Project (chỉ chạy lần đầu, khi chưa có `AK-Docs/03.Testing/05.Scripts/`)
|
|
98
|
+
|
|
99
|
+
AK-Docs vốn là docs-only repo (không có `package.json`). Vì tester yêu cầu script phải nằm trong AK-Docs để dễ trace cùng report/bug, ta scaffold một Playwright project bên trong `03.Testing/05.Scripts/` — **một package.json/node_modules chung cho tất cả repo**, mỗi repo có `playwright.config.ts` riêng (baseURL khác nhau):
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
AK-Docs/03.Testing/05.Scripts/
|
|
103
|
+
├── package.json # 1 lần duy nhất, dùng chung mọi repo
|
|
104
|
+
├── tsconfig.json
|
|
105
|
+
├── node_modules/ # gitignored
|
|
106
|
+
├── Shared/
|
|
107
|
+
│ ├── fixtures/test.ts # test fixture chung
|
|
108
|
+
│ ├── BasePage.ts
|
|
109
|
+
│ └── evidence-helper.ts # xem templates/evidence-helper.ts
|
|
110
|
+
└── {repo}/
|
|
111
|
+
├── playwright.config.ts # baseURL riêng cho repo này
|
|
112
|
+
├── pages/{ScreenID}Page.ts
|
|
113
|
+
└── {featureDir}/{ScreenID}.spec.ts
|
|
114
|
+
```
|
|
54
115
|
|
|
55
|
-
|
|
116
|
+
- `package.json`: devDependencies `@playwright/test`, `typescript`. Tạo bằng `npm init -y` rồi `npm install -D @playwright/test typescript`.
|
|
117
|
+
- Copy `custom/skills/execute-flow/templates/playwright.config.ts` → `AK-Docs/03.Testing/05.Scripts/{repo}/playwright.config.ts`, điền `baseURL` = BASE_URL của repo đó.
|
|
118
|
+
- Copy `custom/skills/execute-flow/templates/evidence-helper.ts` → `AK-Docs/03.Testing/05.Scripts/Shared/evidence-helper.ts` (không tạo lại nếu đã tồn tại).
|
|
119
|
+
- `featureDir` = `{ScreenID}_{Screen-Name-kebab-case}` (vd `AD10_create-product`), khớp naming convention của `docs/common/Testing-Structure.md`.
|
|
56
120
|
|
|
57
|
-
|
|
121
|
+
> **Vì sao không dùng node_modules riêng cho mỗi repo:** tránh cài `@playwright/test` + Chromium nhiều lần, chỉ cần 1 lần install ở `05.Scripts/` là chạy được cho toàn bộ project.
|
|
58
122
|
|
|
59
123
|
### Bước 1: Parse TC file
|
|
60
124
|
|
|
@@ -102,7 +166,7 @@ Phân loại sơ bộ:
|
|
|
102
166
|
|
|
103
167
|
### Bước 3: Scan scripts hiện tại
|
|
104
168
|
|
|
105
|
-
Tìm `
|
|
169
|
+
Tìm `AK-Docs/03.Testing/05.Scripts/{repo}/{featureDir}/{ScreenID}.spec.ts`:
|
|
106
170
|
- Nếu tồn tại: đọc `@tc-id` comments → xác định TCs đã có script
|
|
107
171
|
- Tính hash cho từng TC → so với `@tc-hash` trong file → xác định `updated` vs `skipped`
|
|
108
172
|
- Nếu không tồn tại: tất cả automation TCs đều là `new`
|
|
@@ -152,8 +216,10 @@ Gọi `script-sync` skill với context:
|
|
|
152
216
|
- `tcFile`: path TC file
|
|
153
217
|
- `repo`: từ Gate 1
|
|
154
218
|
- `screenId`: always lowercase ScreenID (vd `ad10`, not `AD10`) — directory path uses lowercase
|
|
219
|
+
- `featureDir`: `{ScreenID}_{Screen-Name-kebab-case}` (vd `AD10_create-product`)
|
|
155
220
|
- `screenUrl`: hỏi tester nếu chưa biết: "URL path của màn hình {ScreenID} là gì? (vd `/admin/products/create`)"
|
|
156
221
|
- `baseUrl`: từ `BASE_URL` env var
|
|
222
|
+
- `scriptsRoot`: `AK-Docs/03.Testing/05.Scripts/{repo}/` (script + Page Object nằm ở đây)
|
|
157
223
|
|
|
158
224
|
Nhận về: danh sách kết quả per TC.
|
|
159
225
|
|
|
@@ -165,7 +231,7 @@ Gen mới: {N} | Updated: {N} | Skipped: {N}
|
|
|
165
231
|
Manual (Untest): {N} TCs
|
|
166
232
|
Blocked: {N} TCs — xem lý do bên trên
|
|
167
233
|
|
|
168
|
-
→ Review: [
|
|
234
|
+
→ Review: [AK-Docs/03.Testing/05.Scripts/{repo}/{featureDir}/{ScreenID}.spec.ts](AK-Docs/03.Testing/05.Scripts/{repo}/{featureDir}/{ScreenID}.spec.ts)
|
|
169
235
|
→ Type APPROVED to run tests
|
|
170
236
|
→ Type SKIP: {TC_ID} để đánh dấu Pending và tiếp tục
|
|
171
237
|
→ Or provide feedback to fix scripts
|
|
@@ -182,44 +248,50 @@ Chỉ chạy sau Gate 2 APPROVED.
|
|
|
182
248
|
|
|
183
249
|
### Bước 1: Xác định run number
|
|
184
250
|
|
|
185
|
-
Scan `
|
|
251
|
+
Scan `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/` để tìm run folder hiện có (nguồn sự thật cho run number):
|
|
186
252
|
- Nếu không có folder nào → `run-1`
|
|
187
253
|
- Nếu có `run-1`, `run-2` → `run-3`
|
|
188
254
|
|
|
189
|
-
Tạo folder
|
|
255
|
+
Tạo 2 folder song song cho run này (evidence vs report/bug, xem "Cấu trúc thư mục đầu ra"):
|
|
256
|
+
- `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/` — evidence nhị phân (screenshots, video, `trace.zip`)
|
|
257
|
+
- `AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/` — `result.md` + `testreport.md`
|
|
190
258
|
|
|
191
259
|
### Bước 2: Chạy Playwright
|
|
192
260
|
|
|
261
|
+
Script nằm trong `AK-Docs/03.Testing/05.Scripts/{repo}/`, evidence nhị phân (screenshot/video/trace) ghi trực tiếp vào `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/` — dùng env var `EVIDENCE_DIR` để `playwright.config.ts` set `outputDir` động (xem template). Vì `04.Evidence/` và `05.Scripts/` đều là con trực tiếp của `03.Testing/`, chỉ cần đi lên 1 cấp:
|
|
262
|
+
|
|
193
263
|
```bash
|
|
194
|
-
cd
|
|
195
|
-
|
|
264
|
+
cd AK-Docs/03.Testing/05.Scripts
|
|
265
|
+
EVIDENCE_DIR="$(pwd)/../04.Evidence/{repo}/{featureDir}/run-{N}" \
|
|
266
|
+
BASE_URL={baseUrl} \
|
|
267
|
+
npx playwright test --config={repo}/playwright.config.ts {repo}/{featureDir}/{ScreenID}.spec.ts
|
|
196
268
|
```
|
|
197
269
|
|
|
198
|
-
Note: Reporters
|
|
270
|
+
Note: Reporters đã được config trong `playwright.config.ts` (json → `{EVIDENCE_DIR}/results.json`, html, list) — CLI `--reporter` flag sẽ override nên không dùng ở đây.
|
|
199
271
|
|
|
200
272
|
### Bước 3: Parse kết quả
|
|
201
273
|
|
|
202
|
-
Đọc `
|
|
274
|
+
Đọc `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/results.json` (Playwright JSON output, ghi trực tiếp vào evidence dir nhờ `EVIDENCE_DIR`).
|
|
203
275
|
|
|
204
276
|
Với mỗi test:
|
|
205
277
|
- Extract TC_ID từ test title (format: `AD10_001 - {name}` hoặc `[AD10_001] {name}`)
|
|
206
278
|
- Map: TC_ID → `{status: 'passed'|'failed', duration, attachments, errors}`
|
|
207
279
|
|
|
208
|
-
|
|
280
|
+
**Cross-check trước khi ghi PASS/FAIL** (xem "DOM Verification Rules" bên dưới) — không tin tuyệt đối vào exit code của Playwright, phải verify lại assertion có thực sự map đúng Expected Result trong TC file.
|
|
281
|
+
|
|
282
|
+
### Bước 4: Tổ chức Evidence + Report
|
|
209
283
|
|
|
210
284
|
Với mỗi TC:
|
|
211
285
|
|
|
212
|
-
**
|
|
286
|
+
**Evidence (ở `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/{TC_ID}-{kebab-scenario}/`)** — Playwright ghi trực tiếp vào đây nhờ `EVIDENCE_DIR`, không cần copy tay:
|
|
287
|
+
- Screenshots per-step → `step-NN-{desc}.png` (xem "Evidence Quality Rules" — mỗi step trong TC Steps phải có 1 screenshot riêng, không chỉ chụp cuối cùng)
|
|
288
|
+
- Trace file → `trace.zip`
|
|
213
289
|
|
|
214
290
|
Với `kebab-scenario` = Test Case Name lowercase, dấu cách thành `-`, bỏ ký tự đặc biệt.
|
|
215
291
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
**Copy files từ Playwright output:**
|
|
219
|
-
- Screenshots → `step-NN-{desc}.png`
|
|
220
|
-
- Trace file → `trace.zip`
|
|
292
|
+
**Report (`AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/{TC_ID}-{kebab-scenario}/result.md`)** — file text, không chứa binary:
|
|
221
293
|
|
|
222
|
-
|
|
294
|
+
Ngôn ngữ output: auto-detect theo ngôn ngữ của ticket/testcase input — xem `custom/rules/output-language.md` và `custom/skills/test-skills/rules/qa-writing-standards.md` (input tiếng Việt → output tiếng Việt; ngược lại mặc định tiếng Anh; tiêu đề cột bảng vẫn giữ tiếng Anh).
|
|
223
295
|
|
|
224
296
|
Khi PASS:
|
|
225
297
|
```markdown
|
|
@@ -230,6 +302,8 @@ Khi PASS:
|
|
|
230
302
|
**Run:** run-{N} | {YYYY-MM-DD HH:MM}
|
|
231
303
|
|
|
232
304
|
## Evidence
|
|
305
|
+
→ `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/{TC_ID}-{kebab-scenario}/`
|
|
306
|
+
|
|
233
307
|
| Step | Screenshot |
|
|
234
308
|
|------|-----------|
|
|
235
309
|
| {step 1} | step-01-{desc}.png |
|
|
@@ -248,20 +322,22 @@ Khi FAIL:
|
|
|
248
322
|
**Error:** {error message từ Playwright}
|
|
249
323
|
|
|
250
324
|
## Evidence
|
|
325
|
+
→ `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/{TC_ID}-{kebab-scenario}/`
|
|
326
|
+
|
|
251
327
|
| File | Mô tả |
|
|
252
328
|
|------|-------|
|
|
253
329
|
| failure-screenshot.png | Trạng thái lúc fail |
|
|
254
330
|
| trace.zip | Playwright trace |
|
|
255
331
|
|
|
256
332
|
## Bug Reference
|
|
257
|
-
→ Xem:
|
|
333
|
+
→ Xem: `AK-Docs/03.Testing/06.Bugs/{repo}/{featureDir}/run-{N}/BUG-{NNN}-{slug}.md`
|
|
258
334
|
```
|
|
259
335
|
|
|
260
|
-
Với Manual TCs (Untest): không tạo evidence folder.
|
|
336
|
+
Với Manual TCs (Untest): không tạo evidence folder / result.md.
|
|
261
337
|
|
|
262
338
|
### Bước 5: Auto-draft Bug Reports
|
|
263
339
|
|
|
264
|
-
Với mỗi TC failed, tạo `
|
|
340
|
+
Với mỗi TC failed, tạo `AK-Docs/03.Testing/06.Bugs/{repo}/{featureDir}/run-{N}/BUG-{NNN}-{slug}.md`:
|
|
265
341
|
|
|
266
342
|
```markdown
|
|
267
343
|
# BUG-{NNN} — {Test Case Name}
|
|
@@ -288,8 +364,8 @@ Với mỗi TC failed, tạo `ak-test/{repo}/results/{screenId}/run-{N}/bugs/BUG
|
|
|
288
364
|
|
|
289
365
|
## Evidence
|
|
290
366
|
|
|
291
|
-
- Screenshot: `{TC_ID}-{scenario}/failure-screenshot.png`
|
|
292
|
-
- Playwright trace: `{TC_ID}-{scenario}/trace.zip`
|
|
367
|
+
- Screenshot: `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/{TC_ID}-{scenario}/failure-screenshot.png`
|
|
368
|
+
- Playwright trace: `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/{TC_ID}-{scenario}/trace.zip`
|
|
293
369
|
|
|
294
370
|
## Technical Notes
|
|
295
371
|
|
|
@@ -305,7 +381,31 @@ Với mỗi TC failed, tạo `ak-test/{repo}/results/{screenId}/run-{N}/bugs/BUG
|
|
|
305
381
|
- [ ] Retest: `RETEST: {TC_ID}`
|
|
306
382
|
```
|
|
307
383
|
|
|
308
|
-
BUG số: đếm file trong `
|
|
384
|
+
BUG số: đếm file trong `AK-Docs/03.Testing/06.Bugs/{repo}/{featureDir}/run-{N}/` folder, +1. Format: `001`, `002`,...
|
|
385
|
+
|
|
386
|
+
### DOM Verification Rules (bắt buộc — tránh false pass/fail)
|
|
387
|
+
|
|
388
|
+
Script-sync (Gate 2) chịu trách nhiệm sinh assertion đúng, nhưng Gate 3 vẫn phải verify lại trước khi ghi kết quả — vì nguyên nhân phổ biến khiến "TC fail bị báo thành pass" hoặc ngược lại là assertion quá lỏng hoặc chọn nhầm phần tử, không phải do Playwright chạy sai:
|
|
389
|
+
|
|
390
|
+
- ❌ **Không** coi "test không throw exception" = pass. Phải có ít nhất 1 `expect(...)` khẳng định đúng **nội dung** Expected Result của TC (text, giá trị, trạng thái field, số lượng item...), không chỉ `toBeVisible()` chung.
|
|
391
|
+
- ❌ **Không** dùng locator quá rộng (vd `page.locator('.modal')` khi có nhiều `.modal` trên trang, hoặc `page.locator('button').first()` khi có nhiều button cùng loại) — nếu locator match nhiều hơn 1 element, đây chính là nguyên nhân hay gặp khiến assertion pass "nhầm" vào element khác không phải element cần kiểm tra. Dùng `data-testid`/`id` cụ thể hoặc scope locator trong đúng container (`page.locator('#order-detail').getByText(...)`).
|
|
392
|
+
- ❌ **Không** dùng `try/catch` nuốt lỗi rồi coi là pass nếu catch block không re-throw hoặc không có assertion thay thế.
|
|
393
|
+
- ✅ Khi TC yêu cầu "hiển thị message lỗi X" → assertion phải check đúng text X (`toHaveText`/`toContainText`), không chỉ check element lỗi tồn tại.
|
|
394
|
+
- ✅ Khi TC yêu cầu "field Y bị disable/readonly" → assertion phải là `toBeDisabled()`/`toHaveAttribute('readonly', ...)`, không phải `toBeVisible()`.
|
|
395
|
+
- ✅ Sau khi Playwright báo `passed`, nếu kịch bản TC là **negative/validation case** (kỳ vọng lỗi/chặn hành động) mà spec không có assertion kiểm tra đúng behavior chặn đó → đánh dấu **NEEDS_REVIEW** trong result.md thay vì tin theo status "passed" của Playwright, và note lại trong Gate 3 summary để tester tự kiểm tra bằng mắt qua evidence.
|
|
396
|
+
- ✅ Nếu Playwright báo `failed` do lỗi hạ tầng (timeout do element chưa load kịp, network flake, không phải do app sai) → thử lại tối đa 1 lần (`--retries=1` đã có trong config) trước khi kết luận FAIL và draft bug — tránh báo fail giả do timing.
|
|
397
|
+
|
|
398
|
+
### Evidence Quality Rules (bắt buộc)
|
|
399
|
+
|
|
400
|
+
Giải quyết vấn đề "chụp evidence sai thời điểm / mờ / thiếu step / không mở được modal":
|
|
401
|
+
|
|
402
|
+
1. **Thời điểm chụp:** Nếu TC không có yêu cầu cụ thể về thời điểm chụp, luôn gọi `waitForUiSettled(page)` (từ `Shared/evidence-helper.ts`) trước khi screenshot — hàm này chờ `networkidle` **và** không còn spinner/loading indicator (`[data-loading]`, `.spinner`, `.loading`, role `progressbar`...) **và** fonts đã load xong (`document.fonts.ready`). Không chụp ngay sau khi trigger action.
|
|
403
|
+
2. **Chất lượng ảnh:** Dùng `page.screenshot({ fullPage: true, animations: 'disabled', scale: 'css' })` qua helper — không dùng screenshot mặc định của reporter. Config `deviceScaleFactor: 2` trong `playwright.config.ts` để ảnh sắc nét (đặc biệt chữ Kanji/Katakana). Nếu app hiển thị tiếng Nhật, set `locale: 'ja-JP'` trong config để đúng font rendering, và đảm bảo container/CI có font Nhật (Noto Sans JP) cài sẵn — nếu không có, ảnh sẽ hiện ô vuông/mờ chữ dù code đúng.
|
|
404
|
+
3. **Popup/Modal cần click để mở:** Nếu TC Step mô tả hành động mở popup/modal (vd "Click vào item A để mở modal B1") → script **phải thực hiện click đó** rồi `waitForUiSettled` trước khi chụp bước đó — không chụp màn hình danh sách rồi bỏ qua bước mở modal.
|
|
405
|
+
4. **Nội dung có scroll:** Nếu vùng cần chụp (modal, table, page) cao hơn viewport → dùng `scrollIntoViewIfNeeded()` trên phần tử cần confirm trước khi chụp, hoặc chụp `fullPage: true` cho toàn trang nếu modal không giới hạn scroll riêng. Không chụp full page nếu modal có scroll riêng nội bộ — trong trường hợp đó chụp riêng viewport của modal sau khi cuộn tới đúng vị trí.
|
|
406
|
+
5. **Highlight vùng cần confirm:** Trước khi chụp step có 1 item/element cụ thể cần tester chú ý (item vừa click, message lỗi, field vừa thay đổi...) → gọi `highlightElement(page, selector)` (khoanh viền đỏ 3px + không che nội dung) ngay trước `page.screenshot()`, rồi gọi `removeHighlight(page)` ngay sau đó để không ảnh hưởng bước tiếp theo.
|
|
407
|
+
6. **1 screenshot cho mỗi Step có mô tả UI trong TC:** Nếu TC Steps mô tả nhiều bước điều hướng (vd: "1. Trên màn hình danh sách, click item A để mở A1 → 2. Trên A1, click button B để mở modal B1") → chụp **evidence riêng cho từng bước có thay đổi màn hình/state đáng chú ý**, không chỉ chụp kết quả cuối. Với ví dụ trên: `step-01-{...}.png` (màn hình danh sách, khoanh đỏ item A trước khi click) + `step-02-{...}.png` (màn hình A1, khoanh đỏ button B trước khi click) + `step-03-{...}.png` (modal B1 mở ra, khoanh đỏ nội dung cần confirm). File `step-NN-{desc}.png` đặt tên theo đúng thứ tự Step trong TC file.
|
|
408
|
+
7. Dùng helper `captureStepEvidence(page, evidenceDir, stepIndex, stepDesc, { highlightSelector, scrollSelector })` (xem `templates/evidence-helper.ts`) trong mọi spec mới sinh ra ở Gate 2 — không tự viết lại logic wait/highlight/scroll/screenshot rời rạc trong từng test.
|
|
309
409
|
|
|
310
410
|
### Bước 6: Hiển thị Gate 3
|
|
311
411
|
|
|
@@ -321,7 +421,7 @@ Results (run-{N}):
|
|
|
321
421
|
Bugs drafted: {N} ({N} Critical, {N} High, {N} Medium, {N} Low)
|
|
322
422
|
Unresolved critical: {N}
|
|
323
423
|
|
|
324
|
-
→ Evidence:
|
|
424
|
+
→ Evidence: AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/
|
|
325
425
|
→ Type APPROVED when all critical/high bugs resolved
|
|
326
426
|
→ Type RETEST: {TC_ID} to re-execute
|
|
327
427
|
→ Type PENDING: {TC_ID} để đánh dấu TC không chạy được
|
|
@@ -337,11 +437,13 @@ Khi tester gõ `RETEST: AD10_005, AD10_008`:
|
|
|
337
437
|
- Nếu phát hiện commit mới → đề xuất TC bổ sung, tester approve delta trước
|
|
338
438
|
2. **Chạy selective test:**
|
|
339
439
|
```bash
|
|
340
|
-
cd
|
|
341
|
-
|
|
440
|
+
cd AK-Docs/03.Testing/05.Scripts
|
|
441
|
+
EVIDENCE_DIR="$(pwd)/../04.Evidence/{repo}/{featureDir}/run-{N+1}" \
|
|
442
|
+
BASE_URL={baseUrl} \
|
|
443
|
+
npx playwright test --config={repo}/playwright.config.ts --grep "AD10_005|AD10_008"
|
|
342
444
|
```
|
|
343
445
|
3. Xác định run number mới (run-{N+1})
|
|
344
|
-
4. Tổ chức evidence vào `run-{N+1}/` (không xóa run cũ)
|
|
446
|
+
4. Tổ chức evidence vào `AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N+1}/` + report vào `AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N+1}/` (không xóa run cũ)
|
|
345
447
|
5. Cập nhật bug status:
|
|
346
448
|
- TC pass sau retest → ghi `RESOLVED` vào bug file: `**Status:** ✅ Resolved`
|
|
347
449
|
- TC vẫn fail → ghi `**Retest {N}:** Still OPEN` vào bug file
|
|
@@ -356,15 +458,15 @@ Chỉ chạy sau Gate 3 APPROVED.
|
|
|
356
458
|
|
|
357
459
|
### Bước 1: Tổng hợp kết quả
|
|
358
460
|
|
|
359
|
-
Đọc tất cả `result.md` trong `run-{N}/`:
|
|
461
|
+
Đọc tất cả `result.md` trong `AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/`:
|
|
360
462
|
- Count passed/failed/untest/pending per run
|
|
361
463
|
- Identify last run number (run-{max})
|
|
362
464
|
|
|
363
|
-
Đọc `
|
|
465
|
+
Đọc `AK-Docs/03.Testing/06.Bugs/{repo}/{featureDir}/run-{N}/BUG-*.md`: phân loại severity + status (Open/Resolved).
|
|
364
466
|
|
|
365
467
|
### Bước 2: Sinh testreport.md
|
|
366
468
|
|
|
367
|
-
Tạo `
|
|
469
|
+
Tạo `AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/testreport.md` theo template chuẩn:
|
|
368
470
|
|
|
369
471
|
```markdown
|
|
370
472
|
# SUMMARY TEST REPORT
|
|
@@ -404,7 +506,7 @@ Hỏi từng bug theo thứ tự severity (Critical → High → Medium → Low)
|
|
|
404
506
|
|
|
405
507
|
```
|
|
406
508
|
🐛 BUG-{NNN} [{Severity}] — {Title}
|
|
407
|
-
TC: {TC_ID} |
|
|
509
|
+
TC: {TC_ID} | Bug: AK-Docs/03.Testing/06.Bugs/{repo}/{featureDir}/run-{N}/BUG-{NNN}... | Evidence: AK-Docs/03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}/{TC_ID}-{scenario}/
|
|
408
510
|
Steps: {steps tóm tắt}
|
|
409
511
|
Expected: {expected}
|
|
410
512
|
Actual: {actual}
|
|
@@ -428,7 +530,7 @@ Passed: {N}/{total} | % hoàn thành: {N}%
|
|
|
428
530
|
Bugs logged to Jira: {N}/{total bugs}
|
|
429
531
|
Recommendation: {Go / No-go / Conditional go}
|
|
430
532
|
|
|
431
|
-
→ Review: [
|
|
533
|
+
→ Review: [AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/testreport.md](AK-Docs/03.Testing/02.Reports/{repo}/{featureDir}/run-{N}/testreport.md)
|
|
432
534
|
→ Type APPROVED to sign off
|
|
433
535
|
→ Type NO-GO: {reason} to reject release
|
|
434
536
|
|
|
@@ -466,7 +568,11 @@ SKIP-BUG: BUG-{NNN} # bỏ qua không log bug này
|
|
|
466
568
|
## Mandatory Rules
|
|
467
569
|
|
|
468
570
|
- ✅ Dừng ở mỗi gate, chờ APPROVED trước khi proceed
|
|
469
|
-
- ✅ `run-{N}` không bao giờ bị xóa hay overwrite — luôn tạo folder mới
|
|
571
|
+
- ✅ `run-{N}` không bao giờ bị xóa hay overwrite — luôn tạo folder mới (cả ở `04.Evidence/`, `02.Reports/` và `06.Bugs/`)
|
|
470
572
|
- ✅ Hỏi từng bug một — không auto-log toàn bộ
|
|
573
|
+
- ✅ Evidence nhị phân (screenshot/video/`trace.zip`) luôn ở `AK-Docs/03.Testing/04.Evidence/` — **phải nằm trong `.gitignore`**, không commit lên Git
|
|
574
|
+
- ✅ Script/result/report/bug (text) luôn ở `AK-Docs/03.Testing/{05.Scripts,02.Reports,06.Bugs}/` — không dùng `ak-test/` nữa
|
|
575
|
+
- ✅ Trước khi ghi PASS/FAIL vào result.md, verify lại assertion theo "DOM Verification Rules" — không tin tuyệt đối exit code Playwright
|
|
576
|
+
- ✅ Mọi spec mới sinh dùng `captureStepEvidence()` từ `Shared/evidence-helper.ts` cho evidence — không viết lại logic wait/highlight/scroll rời rạc
|
|
471
577
|
- ❌ Không lấy expected result từ PR/code — chỉ từ TC file
|
|
472
578
|
- ❌ Không skip gate kể cả khi "chỉ có 1 TC"
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { Page, Locator, TestInfo } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared evidence helpers for generated Playwright specs (execute-flow / script-sync skills).
|
|
5
|
+
* Copied once to AK-Docs/03.Testing/05.Scripts/Shared/evidence-helper.ts — do not regenerate
|
|
6
|
+
* if it already exists; hand edits (e.g. project-specific loading selectors) must survive re-sync.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** CSS selectors treated as "still loading" — extend this list per-project if needed. */
|
|
10
|
+
const LOADING_SELECTORS = [
|
|
11
|
+
'[data-loading="true"]',
|
|
12
|
+
'.spinner',
|
|
13
|
+
'.loading',
|
|
14
|
+
'.skeleton',
|
|
15
|
+
'[role="progressbar"]',
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Waits until the page is visually stable before a screenshot is taken:
|
|
20
|
+
* network idle, no known loading indicator visible, and web fonts finished loading
|
|
21
|
+
* (prevents blurry / tofu-box Kanji text in evidence screenshots).
|
|
22
|
+
*/
|
|
23
|
+
export async function waitForUiSettled(page: Page, timeoutMs = 15_000): Promise<void> {
|
|
24
|
+
await page.waitForLoadState('networkidle', { timeout: timeoutMs }).catch(() => {})
|
|
25
|
+
|
|
26
|
+
await page.waitForFunction(
|
|
27
|
+
(selectors) =>
|
|
28
|
+
selectors.every((sel) => {
|
|
29
|
+
const el = document.querySelector(sel)
|
|
30
|
+
if (!el) return true
|
|
31
|
+
const style = window.getComputedStyle(el)
|
|
32
|
+
return style.display === 'none' || style.visibility === 'hidden'
|
|
33
|
+
}),
|
|
34
|
+
LOADING_SELECTORS,
|
|
35
|
+
{ timeout: timeoutMs },
|
|
36
|
+
).catch(() => {})
|
|
37
|
+
|
|
38
|
+
await page.evaluate(() => (document as any).fonts?.ready).catch(() => {})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Draws a 3px solid red outline around `target` without shifting layout (outline, not border). */
|
|
42
|
+
export async function highlightElement(target: Locator): Promise<void> {
|
|
43
|
+
await target.evaluate((el: HTMLElement) => {
|
|
44
|
+
el.setAttribute('data-evidence-highlight', '1')
|
|
45
|
+
el.style.outline = '3px solid #ff0000'
|
|
46
|
+
el.style.outlineOffset = '2px'
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Removes the highlight applied by `highlightElement` so it doesn't leak into the next step's screenshot. */
|
|
51
|
+
export async function removeHighlight(target: Locator): Promise<void> {
|
|
52
|
+
await target
|
|
53
|
+
.evaluate((el: HTMLElement) => {
|
|
54
|
+
el.style.outline = ''
|
|
55
|
+
el.style.outlineOffset = ''
|
|
56
|
+
el.removeAttribute('data-evidence-highlight')
|
|
57
|
+
})
|
|
58
|
+
.catch(() => {})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface CaptureStepOptions {
|
|
62
|
+
/** Element to draw a red rectangle around before the screenshot (the item/content the tester must confirm). */
|
|
63
|
+
highlightSelector?: Locator
|
|
64
|
+
/** Element to scroll into view before the screenshot (for content inside a scrollable modal/table). */
|
|
65
|
+
scrollSelector?: Locator
|
|
66
|
+
/** Screenshot the full page instead of the current viewport. Default: true. */
|
|
67
|
+
fullPage?: boolean
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Captures one evidence screenshot for one TC Step, following the naming convention
|
|
72
|
+
* `step-NN-{desc}.png` inside the run's evidence dir (Playwright's configured outputDir,
|
|
73
|
+
* i.e. `EVIDENCE_DIR` — see playwright.config.ts template).
|
|
74
|
+
*
|
|
75
|
+
* Call this once per TC Step that changes UI state or navigates to a new screen/modal —
|
|
76
|
+
* not just once at the end of the test.
|
|
77
|
+
*/
|
|
78
|
+
export async function captureStepEvidence(
|
|
79
|
+
page: Page,
|
|
80
|
+
testInfo: TestInfo,
|
|
81
|
+
stepIndex: number,
|
|
82
|
+
stepDesc: string,
|
|
83
|
+
opts: CaptureStepOptions = {},
|
|
84
|
+
): Promise<void> {
|
|
85
|
+
await waitForUiSettled(page)
|
|
86
|
+
|
|
87
|
+
if (opts.scrollSelector) {
|
|
88
|
+
await opts.scrollSelector.scrollIntoViewIfNeeded()
|
|
89
|
+
await waitForUiSettled(page)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (opts.highlightSelector) {
|
|
93
|
+
await highlightElement(opts.highlightSelector)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const slug = stepDesc
|
|
97
|
+
.toLowerCase()
|
|
98
|
+
.normalize('NFD')
|
|
99
|
+
.replace(/[̀-ͯ]/g, '')
|
|
100
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
101
|
+
.replace(/(^-|-$)/g, '')
|
|
102
|
+
const fileName = `step-${String(stepIndex).padStart(2, '0')}-${slug}.png`
|
|
103
|
+
|
|
104
|
+
await page.screenshot({
|
|
105
|
+
path: testInfo.outputPath(fileName),
|
|
106
|
+
fullPage: opts.fullPage ?? true,
|
|
107
|
+
animations: 'disabled',
|
|
108
|
+
scale: 'css',
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if (opts.highlightSelector) {
|
|
112
|
+
await removeHighlight(opts.highlightSelector)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Convenience wrapper for steps that click a trigger to open a popup/modal, then capture
|
|
118
|
+
* the resulting state. Ensures the click happens BEFORE the screenshot — a common source
|
|
119
|
+
* of "modal never shown in evidence" bugs.
|
|
120
|
+
*/
|
|
121
|
+
export async function openAndCapture(
|
|
122
|
+
page: Page,
|
|
123
|
+
testInfo: TestInfo,
|
|
124
|
+
trigger: Locator,
|
|
125
|
+
resultLocator: Locator,
|
|
126
|
+
stepIndex: number,
|
|
127
|
+
stepDesc: string,
|
|
128
|
+
opts: CaptureStepOptions = {},
|
|
129
|
+
): Promise<void> {
|
|
130
|
+
await highlightElement(trigger)
|
|
131
|
+
await page.screenshot({
|
|
132
|
+
path: testInfo.outputPath(`step-${String(stepIndex).padStart(2, '0')}-before-click.png`),
|
|
133
|
+
fullPage: opts.fullPage ?? true,
|
|
134
|
+
animations: 'disabled',
|
|
135
|
+
})
|
|
136
|
+
await removeHighlight(trigger)
|
|
137
|
+
|
|
138
|
+
await trigger.click()
|
|
139
|
+
await resultLocator.waitFor({ state: 'visible' })
|
|
140
|
+
|
|
141
|
+
await captureStepEvidence(page, testInfo, stepIndex, stepDesc, {
|
|
142
|
+
...opts,
|
|
143
|
+
highlightSelector: opts.highlightSelector ?? resultLocator,
|
|
144
|
+
})
|
|
145
|
+
}
|
|
@@ -3,24 +3,44 @@ import path from 'path'
|
|
|
3
3
|
|
|
4
4
|
const LOGGED_OUT = { storageState: undefined }
|
|
5
5
|
|
|
6
|
+
// Script lives in AK-Docs/03.Testing/05.Scripts/{repo}/ but binary evidence (screenshots,
|
|
7
|
+
// video, trace.zip, results.json) must land in AK-Docs/03.Testing/04.Evidence/ instead —
|
|
8
|
+
// that folder is gitignored (binaries, never committed) while this script folder is not.
|
|
9
|
+
// See execute-flow SKILL.md "Cấu trúc thư mục đầu ra". The AI sets EVIDENCE_DIR per run
|
|
10
|
+
// (03.Testing/04.Evidence/{repo}/{featureDir}/run-{N}) when invoking `npx playwright test`.
|
|
11
|
+
const evidenceDir = process.env.EVIDENCE_DIR
|
|
12
|
+
? path.resolve(process.env.EVIDENCE_DIR)
|
|
13
|
+
: path.resolve(__dirname, 'test-results')
|
|
14
|
+
|
|
6
15
|
export default defineConfig({
|
|
7
|
-
testDir: '
|
|
8
|
-
outputDir:
|
|
16
|
+
testDir: '.',
|
|
17
|
+
outputDir: evidenceDir,
|
|
9
18
|
fullyParallel: false,
|
|
10
19
|
forbidOnly: !!process.env.CI,
|
|
11
|
-
retries:
|
|
20
|
+
retries: 1, // absorb transient timing flakes before reporting FAIL — see "DOM Verification Rules"
|
|
12
21
|
workers: 1,
|
|
13
22
|
reporter: [
|
|
14
|
-
['json', { outputFile: '
|
|
15
|
-
['html', { outputFolder: '
|
|
23
|
+
['json', { outputFile: path.join(evidenceDir, 'results.json') }],
|
|
24
|
+
['html', { outputFolder: path.join(evidenceDir, 'html-report'), open: 'never' }],
|
|
16
25
|
['list'],
|
|
17
26
|
],
|
|
18
27
|
use: {
|
|
19
28
|
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
|
20
|
-
trace: 'on-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
trace: 'retain-on-failure',
|
|
30
|
+
// Manual per-step screenshots come from captureStepEvidence() in evidence-helper.ts —
|
|
31
|
+
// this stays 'only-on-failure' so it doesn't duplicate/clash with those step-NN-*.png files.
|
|
32
|
+
screenshot: 'only-on-failure',
|
|
33
|
+
video: 'retain-on-failure',
|
|
23
34
|
headless: !process.env.HEADED,
|
|
35
|
+
// Higher device scale factor = sharper text in evidence, especially Kanji/Katakana.
|
|
36
|
+
deviceScaleFactor: 2,
|
|
37
|
+
// Set to the app's actual UI locale so date/number formatting and font fallback match
|
|
38
|
+
// what the browser renders in production — mismatched locale is a common cause of
|
|
39
|
+
// "screenshot color/rendering doesn't match what I see in my own browser" reports.
|
|
40
|
+
locale: process.env.UI_LOCALE || 'ja-JP',
|
|
41
|
+
viewport: { width: 1440, height: 900 },
|
|
42
|
+
// Stops CSS/JS animations from being mid-transition when a screenshot fires.
|
|
43
|
+
reducedMotion: 'reduce',
|
|
24
44
|
},
|
|
25
45
|
projects: [
|
|
26
46
|
// Uncomment nếu cần auth setup:
|
|
@@ -22,8 +22,7 @@ Before reading the ticket in depth, verify the bridge document from UC Spec —
|
|
|
22
22
|
3. Check `AK-Docs/02.BA-Specs/00.Requirements/[functionId]/System-Requirement_v*.md`:
|
|
23
23
|
- **Missing** → `⚠️ CẢNH BÁO: Không tìm thấy System Requirement cho [functionId]. Khuyến nghị chạy "ak use" → "📐 Create System Requirement" trước ticket kế tiếp — Gate 1 vẫn tiếp tục ngay bây giờ dựa trên UC Spec/ticket.`
|
|
24
24
|
- **Exists, but its `UC-Spec-Version` header doesn't match the UC Spec found in step 2** → `⚠️ CẢNH BÁO: System Requirement đang trace theo UC Spec v[X], UC Spec hiện tại là v[Y] — nội dung có thể lỗi thời.` Treat its items as Assumption rather than Fact in Step 1.75.
|
|
25
|
-
- **Exists
|
|
26
|
-
- **Exists, matches, and Approved** → continue silently. Keep this file's path — Step 1 must read it as mandatory input.
|
|
25
|
+
- **Exists and matches** → continue silently. Keep this file's path — Step 1 must read it as mandatory input. (Its presence in the repo already means DEV approved and pushed it — approval isn't tracked with a separate header.)
|
|
27
26
|
|
|
28
27
|
Any warning raised above must also be written into this ticket's requirement doc (Section 1, Facts/Assumptions/Gaps in Step 4) — not just printed and forgotten. If DEV wants the gap closed properly, they can run `ak use` → "📐 Create System Requirement" for `[functionId]` at any point (before or after this ticket) — it's a separate 2-gate task type, not an inline sub-skill here.
|
|
29
28
|
|