@educa-corp/sdd-framework 0.9.6 → 0.9.8
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/bin/lint-trace.js +4 -4
- package/bin/qc-base-map.json +13 -11
- package/bin/self-check.js +497 -16
- package/bin/trace-schema.json +3226 -2656
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/amend-prd.md +7 -1
- package/core/commands/debug.md +8 -2
- package/core/commands/define-product.md +38 -1
- package/core/commands/dev-gen-test.md +9 -3
- package/core/commands/dev-run-test.md +8 -2
- package/core/commands/dev-smoke-test.md +7 -1
- package/core/commands/extend-prd.md +7 -1
- package/core/commands/fix-bug.md +11 -5
- package/core/commands/generate-architecture.md +9 -1
- package/core/commands/generate-bdd.md +45 -5
- package/core/commands/generate-code.md +43 -4
- package/core/commands/generate-design-spec.md +7 -1
- package/core/commands/generate-prd.md +9 -1
- package/core/commands/generate-spec-manifest.md +7 -1
- package/core/commands/generate-tech-docs.md +41 -1
- package/core/commands/learn.md +7 -1
- package/core/commands/map-testids.md +11 -5
- package/core/commands/propose-scenario.md +7 -1
- package/core/commands/qc-analyze.md +12 -6
- package/core/commands/qc-automation-assess.md +356 -0
- package/core/commands/qc-design-script.md +430 -0
- package/core/commands/qc-design-test.md +98 -20
- package/core/commands/qc-plan.md +9 -3
- package/core/commands/qc-report.md +92 -77
- package/core/commands/qc-review-script.md +342 -0
- package/core/commands/{qc-review.md → qc-review-testcase.md} +86 -54
- package/core/commands/qc-run-manualtest.md +401 -0
- package/core/commands/qc-run-script.md +421 -0
- package/core/commands/refine-prd.md +7 -1
- package/core/commands/report-bug.md +9 -3
- package/core/commands/review-code.md +9 -3
- package/core/commands/review-context.md +11 -3
- package/core/commands/review-tech-docs.md +11 -3
- package/core/commands/setup-ai-first.md +7 -1
- package/core/commands/validate-traces.md +10 -4
- package/core/modules/qc-playwright-ts/module.yaml +13 -0
- package/core/modules/qc-playwright-ts/stack-profile.yaml +99 -0
- package/core/modules/qc-wdio-appium/module.yaml +20 -0
- package/core/modules/qc-wdio-appium/stack-profile.yaml +107 -0
- package/core/rules/workflow.md +2 -2
- package/core/skills/qc/_shared/self-review-principles.md +2 -2
- package/core/skills/qc/qa-analyst/DOC_GAP.template.md +1 -1
- package/core/skills/qc/qa-analyst/data-flow.md +1 -1
- package/core/skills/qc/qa-analyst/spec-issue-reporter.md +1 -1
- package/core/skills/qc/qa-automation-assess/matrix.md +123 -0
- package/core/skills/qc/qa-designer/e2e/journey.md +1 -1
- package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +1 -1
- package/core/skills/qc/{qa-runner → qa-designer}/exploratory/session.md +8 -2
- package/core/skills/qc/qa-designer/functional/api.md +2 -2
- package/core/skills/qc/qa-designer/functional/gui-feature.md +1 -1
- package/core/skills/qc/qa-designer/functional/gui-screen.md +1 -1
- package/core/skills/qc/qa-designer/functional/job.md +128 -0
- package/core/skills/qc/qa-designer/integration/api.md +2 -2
- package/core/skills/qc/qa-designer/integration/db.md +2 -2
- package/core/skills/qc/qa-designer/integration/gui.md +1 -1
- package/core/skills/qc/qa-designer/integration/{kafka.md → queue.md} +21 -5
- package/core/skills/qc/qa-designer/non-functional.md +1 -1
- package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +17 -0
- package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +28 -6
- package/core/skills/qc/qa-reviewer/script/_shared/review-rules.md +121 -0
- package/core/skills/qc/qa-reviewer/script/api/auth.md +49 -0
- package/core/skills/qc/qa-reviewer/script/api/endpoint.md +89 -0
- package/core/skills/qc/qa-reviewer/script/api/security.md +46 -0
- package/core/skills/qc/qa-reviewer/script/exploratory.md +3 -3
- package/core/skills/qc/qa-reviewer/script/mobile/e2e.md +41 -0
- package/core/skills/qc/qa-reviewer/script/mobile/functional.md +90 -0
- package/core/skills/qc/qa-reviewer/script/mobile/integration.md +41 -0
- package/core/skills/qc/qa-reviewer/script/mobile/non-functional.md +43 -0
- package/core/skills/qc/qa-reviewer/script/web/e2e.md +46 -0
- package/core/skills/qc/qa-reviewer/script/web/functional.md +111 -0
- package/core/skills/qc/qa-reviewer/script/web/integration.md +46 -0
- package/core/skills/qc/qa-reviewer/script/web/non-functional.md +49 -0
- package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +1 -1
- package/core/skills/qc/qa-reviewer/shared/review-file-template.md +29 -10
- package/core/skills/qc/qa-reviewer/test-case/e2e.md +2 -2
- package/core/skills/qc/qa-reviewer/test-case/exploratory.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/functional.md +2 -2
- package/core/skills/qc/qa-reviewer/test-case/integration.md +2 -2
- package/core/skills/qc/qa-reviewer/test-case/non-functional.md +2 -2
- package/core/skills/qc/qa-script-designer/_shared/api-conventions.md +94 -0
- package/core/skills/qc/qa-script-designer/_shared/file-naming-and-folders.md +109 -0
- package/core/skills/qc/qa-script-designer/_shared/mobile-conventions.md +196 -0
- package/core/skills/qc/qa-script-designer/_shared/web-conventions.md +257 -0
- package/core/skills/qc/qa-script-designer/api/auth.md +43 -0
- package/core/skills/qc/qa-script-designer/api/endpoint.md +61 -0
- package/core/skills/qc/qa-script-designer/api/security.md +41 -0
- package/core/skills/qc/qa-script-designer/mobile/e2e.md +35 -0
- package/core/skills/qc/qa-script-designer/mobile/functional/feature.md +32 -0
- package/core/skills/qc/qa-script-designer/mobile/functional/screen.md +42 -0
- package/core/skills/qc/qa-script-designer/mobile/integration.md +39 -0
- package/core/skills/qc/qa-script-designer/mobile/non-functional.md +39 -0
- package/core/skills/qc/qa-script-designer/web/e2e.md +36 -0
- package/core/skills/qc/qa-script-designer/web/functional/api.md +39 -0
- package/core/skills/qc/qa-script-designer/web/functional/gui-feature.md +34 -0
- package/core/skills/qc/qa-script-designer/web/functional/gui-screen.md +42 -0
- package/core/skills/qc/qa-script-designer/web/integration.md +43 -0
- package/core/skills/qc/qa-script-designer/web/non-functional.md +42 -0
- package/core/skills/qc/qa-script-runner/mobile/run.md +38 -0
- package/core/skills/qc/qa-script-runner/report.md +41 -0
- package/core/skills/qc/qa-script-runner/web/run.md +48 -0
- package/core/steps/context-loader.md +1 -1
- package/core/steps/gate.md +7 -1
- package/core/steps/qc-scope.md +45 -2
- package/core/steps/qc-stamp.md +4 -4
- package/core/steps/report-footer.md +10 -9
- package/docs/02-concepts/pipeline-steps/07-dev-selftest.md +1 -1
- package/docs/02-concepts/pipeline-steps/08-qc-automation.md +13 -12
- package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +3 -3
- package/docs/02-concepts/traceability.md +1 -1
- package/docs/03-guides/developer.md +1 -1
- package/docs/03-guides/tester-qa.md +40 -11
- package/docs/04-reference/commands.md +4 -2
- package/docs/04-reference/modules.md +2 -1
- package/docs/04-reference/trace-schema.md +4 -4
- package/docs/explain/17-qc-design-test.md +5 -5
- package/docs/explain/18-qc-review.md +42 -20
- package/docs/explain/19-qc-run-test.md +13 -10
- package/docs/explain/20-qc-report.md +3 -3
- package/docs/explain/23-fix-bug.md +2 -2
- package/docs/explain/README.md +2 -2
- package/docs/plans/qc-surgery/01-checklist.md +86 -21
- package/docs/plans/qc-surgery/PLAN_v2.md +295 -0
- package/docs/plans/qc-surgery/exec-S-ap-stack-typescript.md +420 -0
- package/docs/plans/qc-surgery/exec-S0-guard-cam-stack-cu.md +400 -0
- package/docs/plans/qc-surgery/exec-S1-hai-module-thay-qc-playwright.md +267 -0
- package/docs/plans/qc-surgery/exec-S2-qa-runner-thanh-script-designer-runner.md +340 -0
- package/docs/plans/qc-surgery/exec-S3-viet-lai-tieu-chi-review-script.md +322 -0
- package/docs/plans/qc-surgery/exec-S5-an-theo-don-dau-vet-stack-cu.md +292 -0
- package/package.json +1 -1
- package/core/commands/qc-run-test.md +0 -561
- package/core/modules/qc-playwright/stack-profile.yaml +0 -66
- package/core/skills/qc/qa-reviewer/script/e2e.md +0 -95
- package/core/skills/qc/qa-reviewer/script/functional.md +0 -109
- package/core/skills/qc/qa-reviewer/script/integration.md +0 -99
- package/core/skills/qc/qa-reviewer/script/non-functional.md +0 -134
- package/core/skills/qc/qa-runner/e2e.md +0 -49
- package/core/skills/qc/qa-runner/functional/api.md +0 -35
- package/core/skills/qc/qa-runner/functional/gui-feature.md +0 -57
- package/core/skills/qc/qa-runner/functional/gui-screen.md +0 -61
- package/core/skills/qc/qa-runner/integration.md +0 -47
- package/core/skills/qc/qa-runner/non-functional.md +0 -49
- package/core/skills/qc/qa-runner/report/report.md +0 -37
|
@@ -12,7 +12,7 @@ Review bộ functional TC và đánh giá chất lượng.
|
|
|
12
12
|
|
|
13
13
|
## Khi nào trigger
|
|
14
14
|
- "review TC cho [Feature]" / "check coverage"
|
|
15
|
-
- Sau khi qa-designer xong, trước khi PO approve hoặc trước khi /qc-
|
|
15
|
+
- Sau khi qa-designer xong, trước khi PO approve hoặc trước khi /qc-design-script
|
|
16
16
|
|
|
17
17
|
## Khi KHÔNG trigger
|
|
18
18
|
- Review Python script → dùng qa-reviewer
|
|
@@ -117,7 +117,7 @@ hàng của tầng mình, KHÔNG ghi đè tầng khác).
|
|
|
117
117
|
Mỗi tiêu chí: ✅ PASS | ⚠️ WARN | ❌ FAIL + evidence cụ thể
|
|
118
118
|
|
|
119
119
|
**Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. ≥80 đạt · 60–79 cần cải thiện · <60 không đạt.
|
|
120
|
-
**Verdict:**
|
|
120
|
+
**Verdict:** suy từ **số đếm lỗi** theo `shared/review-file-template.md` §Verdict — `≥1 BLOCKER` → `REJECTED` · `≥1 MAJOR` → `REVISION_REQUIRED` · chỉ MINOR/SUGGESTION → `APPROVED_WITH_SUGGESTIONS` · sạch → `APPROVED`. Điểm `XX/100` vẫn ghi, nhưng **không quyết verdict**.
|
|
121
121
|
|
|
122
122
|
Đề xuất TC cần thêm/sửa/xoá, sắp theo priority. Liệt kê TC thiếu Trace BR (⚠️) cần bổ sung.
|
|
123
123
|
|
|
@@ -12,7 +12,7 @@ Review bộ TC tích hợp (GUI↔Backend, API, DB) và đánh giá chất lư
|
|
|
12
12
|
|
|
13
13
|
## Khi nào trigger
|
|
14
14
|
- "review TC integration cho [Feature]" / "check TC tích hợp"
|
|
15
|
-
- Sau khi qa-designer/integration/* xong, trước khi /qc-
|
|
15
|
+
- Sau khi qa-designer/integration/* xong, trước khi /qc-design-script
|
|
16
16
|
|
|
17
17
|
## Khi KHÔNG trigger
|
|
18
18
|
- Review TC functional 1 màn/endpoint → `test-case/functional`
|
|
@@ -113,7 +113,7 @@ hàng của tầng mình, KHÔNG ghi đè tầng khác).
|
|
|
113
113
|
Mỗi tiêu chí: ✅ PASS | ⚠️ WARN | ❌ FAIL + evidence cụ thể (TC ID / điểm tích hợp)
|
|
114
114
|
|
|
115
115
|
**Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. ≥80 đạt · 60–79 cần cải thiện · <60 không đạt.
|
|
116
|
-
**Verdict:**
|
|
116
|
+
**Verdict:** suy từ **số đếm lỗi** theo `shared/review-file-template.md` §Verdict — `≥1 BLOCKER` → `REJECTED` · `≥1 MAJOR` → `REVISION_REQUIRED` · chỉ MINOR/SUGGESTION → `APPROVED_WITH_SUGGESTIONS` · sạch → `APPROVED`. Điểm `XX/100` vẫn ghi, nhưng **không quyết verdict**.
|
|
117
117
|
|
|
118
118
|
Điểm tích hợp thiếu TC; TC Expected mờ nhạt; TC DB thiếu cleanup.
|
|
119
119
|
Điền thêm bảng **Coverage handshake (Integration)** ở cuối file review.
|
|
@@ -12,7 +12,7 @@ Review bộ TC phi chức năng (performance, security, accessibility, compatibi
|
|
|
12
12
|
|
|
13
13
|
## Khi nào trigger
|
|
14
14
|
- "review TC non-functional cho [Feature]" / "check TC hiệu năng/bảo mật/accessibility"
|
|
15
|
-
- Sau khi qa-designer/non-functional xong, trước khi /qc-
|
|
15
|
+
- Sau khi qa-designer/non-functional xong, trước khi /qc-design-script
|
|
16
16
|
|
|
17
17
|
## Khi KHÔNG trigger
|
|
18
18
|
- Review TC chức năng → `test-case/functional`
|
|
@@ -124,7 +124,7 @@ hàng của tầng mình, KHÔNG ghi đè tầng khác).
|
|
|
124
124
|
Mỗi tiêu chí: ✅ PASS | ⚠️ WARN | ❌ FAIL + evidence cụ thể (TC ID)
|
|
125
125
|
|
|
126
126
|
**Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. ≥80 đạt · 60–79 cần cải thiện · <60 không đạt.
|
|
127
|
-
**Verdict:**
|
|
127
|
+
**Verdict:** suy từ **số đếm lỗi** theo `shared/review-file-template.md` §Verdict — `≥1 BLOCKER` → `REJECTED` · `≥1 MAJOR` → `REVISION_REQUIRED` · chỉ MINOR/SUGGESTION → `APPROVED_WITH_SUGGESTIONS` · sạch → `APPROVED`. Điểm `XX/100` vẫn ghi, nhưng **không quyết verdict**.
|
|
128
128
|
|
|
129
129
|
Danh sách TC Expected mờ nhạt (thiếu ngưỡng); loại non-functional thiếu coverage.
|
|
130
130
|
Ghi rõ TC nào cần môi trường đặc biệt. Điền thêm bảng **Chi tiết NFR** ở cuối file review.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-09-17
|
|
4
|
+
source: upstream/qc-base-new/API-Testing-Standards.md §4 §5 §6 §10 §11 · AGT-010 §2 §3 (Approved)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Quy ước viết script API — TypeScript + Playwright (API mode)
|
|
8
|
+
|
|
9
|
+
**Nạp file này trước mọi file lane `api/`.** Nó là bản song song của `web-conventions.md` cho
|
|
10
|
+
nền `system`; hai file **không** kế thừa nhau vì đối tượng khác hẳn.
|
|
11
|
+
|
|
12
|
+
> ⚠️ **API Object ≠ Page Object.** Không màn hình, không locator, không chờ. Mang thói quen web
|
|
13
|
+
> sang đây là viết ra thứ chạy được nhưng sai kiến trúc — và `/qc-review-script` lane `api` sẽ
|
|
14
|
+
> chặn nó bằng `R01`.
|
|
15
|
+
|
|
16
|
+
## 1 · API Object *(§4 · AGT-010 §2.2 · R01)*
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// api/booking.api.ts
|
|
20
|
+
export class BookingAPI extends BaseAPI {
|
|
21
|
+
async create(payload: BookingPayload): Promise<APIResponse> {
|
|
22
|
+
return this.ctx.post('/bookings', { data: payload });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
| Luật | Vi phạm là |
|
|
28
|
+
|---|---|
|
|
29
|
+
| **Mọi HTTP request đi qua API Object** — không `request.post()` thô trong spec | `BLOCKER` |
|
|
30
|
+
| `extends BaseAPI`; class `<Resource>API`; file `<resource>.api.ts` | `MINOR` |
|
|
31
|
+
| **1 method = 1 endpoint action** | `MAJOR` |
|
|
32
|
+
| Trả `Promise<APIResponse>` — **không parse response bên trong** | `MAJOR` |
|
|
33
|
+
| **Không `expect()` trong API Object** — assertion thuộc spec | `MAJOR` |
|
|
34
|
+
| Mỗi resource một API Object, không "God API Object" | `MAJOR` |
|
|
35
|
+
|
|
36
|
+
**Vì sao cấm `expect()` bên trong:** API Object là *nơi biết gọi đâu*, spec là *nơi biết thế nào
|
|
37
|
+
là đúng*. Trộn hai vai thì một thay đổi hợp đồng phải sửa ở hai chỗ, và cái sót lại nói dối.
|
|
38
|
+
|
|
39
|
+
## 2 · Cấu trúc spec — AAA *(§5)*
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
test('TC-API-001 — tạo booking hợp lệ trả 201', async ({ bookingAPI }) => {
|
|
43
|
+
const payload = validBooking; // Arrange
|
|
44
|
+
const res = await bookingAPI.create(payload); // Act
|
|
45
|
+
expect(res.status()).toBe(201); // Assert
|
|
46
|
+
expect(await res.json()).toMatchObject({ id: expect.any(String) });
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- Header block mang **TC-ID** + `@trace.verifies` — thiếu là `BLOCKER` *(`R07`)*
|
|
51
|
+
- Ba khối tách rõ; trộn assert vào giữa chuỗi gọi ⇒ `MINOR`
|
|
52
|
+
- Data-driven bằng `for...of` / `test.each`, không chép test năm lần ⇒ `MINOR`
|
|
53
|
+
|
|
54
|
+
## 3 · Assertion *(§6)*
|
|
55
|
+
|
|
56
|
+
| Bắt buộc | Mức nếu thiếu |
|
|
57
|
+
|---|---|
|
|
58
|
+
| Assert **mã trạng thái** | `BLOCKER` |
|
|
59
|
+
| Assert **cấu trúc + giá trị** của body, không chỉ status | `MAJOR` |
|
|
60
|
+
| Kiểm schema cho response có cấu trúc *(`helpers/schema.helper.ts`)* | `MINOR` |
|
|
61
|
+
| Assert header khi hợp đồng nêu | `MINOR` |
|
|
62
|
+
|
|
63
|
+
`expect(res.ok()).toBeTruthy()` làm assertion **duy nhất** ⇒ `MAJOR` — nó đúng cho mọi 2xx/3xx,
|
|
64
|
+
nên nó không phân biệt được thành công với chuyển hướng.
|
|
65
|
+
|
|
66
|
+
## 4 · Async *(AGT-010 R02)*
|
|
67
|
+
|
|
68
|
+
- **`waitForTimeout` tuyệt đối không dùng** ⇒ `BLOCKER`. API đồng bộ về bản chất: `await` thẳng.
|
|
69
|
+
- Thiếu `await` trước lời gọi ⇒ `BLOCKER` — test kết thúc trước khi có response và **xanh giả**.
|
|
70
|
+
|
|
71
|
+
## 5 · Dữ liệu *(AGT-010 R03)*
|
|
72
|
+
|
|
73
|
+
- Credential · payload · giá trị mong đợi để ở `data/<resource>.data.ts` ⇒ hard-code là `MAJOR`
|
|
74
|
+
- Token qua `fixtures/api.fixture.ts`, phân biệt **theo vai** ⇒ một token cho mọi test là `MAJOR`
|
|
75
|
+
- Bản ghi tạo ra phải xoá ở `afterEach`/`afterAll`, kể cả khi test fail ⇒ thiếu là `MAJOR`
|
|
76
|
+
|
|
77
|
+
## 6 · Đặt tên và đường dẫn
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
api-automation/tests/{TICKET-ID}/<feature>-<scenario>.spec.ts
|
|
81
|
+
api-automation/api/<resource>.api.ts ← PHẲNG, dùng lại xuyên PRD
|
|
82
|
+
api-automation/data/<resource>.data.ts
|
|
83
|
+
api-automation/fixtures/api.fixture.ts
|
|
84
|
+
api-automation/helpers/{schema,auth}.helper.ts
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Dãy ID của lane này **độc lập** với web/mobile *(§7.3)*: `TS-API` · `SCN-API` · `TC-API` ·
|
|
88
|
+
`AUT-API` · `TDS-API`.
|
|
89
|
+
|
|
90
|
+
## 7 · Chất lượng *(§10 · §11)*
|
|
91
|
+
|
|
92
|
+
- TypeScript strict, không `any` ⇒ `MINOR`
|
|
93
|
+
- `test.only` sót lại ⇒ `BLOCKER` *(CI chạy một test rồi báo xanh)*
|
|
94
|
+
- Test flaky phải mang tag `@flaky` và bị tách khỏi suite chính ⇒ không tag là `MAJOR`
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-09-09
|
|
4
|
+
new_in: post-review fix — trước đây `<project>` là placeholder không được định nghĩa, và
|
|
5
|
+
Page Object có 2 quy ước đặt tên mâu thuẫn nhau giữa các file
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Quy ước đặt tên & Folder — Automation Code (nguồn chuẩn duy nhất)
|
|
9
|
+
|
|
10
|
+
Mọi file khác (`_shared/web-conventions.md`, `_shared/mobile-conventions.md`, `qc-design-script.md`,
|
|
11
|
+
`qc-run-script.md`, mọi layer file trong `qa-script-designer/`, `qa-script-runner/`,
|
|
12
|
+
`qa-reviewer/script/`) **tham chiếu file này** thay vì tự lặp lại quy ước path — sửa path chỉ
|
|
13
|
+
sửa ở một chỗ.
|
|
14
|
+
|
|
15
|
+
## 0. Vị trí repo (đã chốt)
|
|
16
|
+
|
|
17
|
+
Automation code nằm trong **repo/module riêng** cho QC — tách biệt spec repo (`spec_source`)
|
|
18
|
+
và repo product code. Root **KHÔNG khai bằng key cấu hình riêng** — nó lấy từ `§layout` của module
|
|
19
|
+
đã phân giải ở `steps/qc-scope.md` §2b: `web`·`webview` → `automation/` · `system` → `api-automation/`
|
|
20
|
+
· `app`·`app-ios`·`app-android` → `mobile-automation/`.
|
|
21
|
+
|
|
22
|
+
> **Vì sao không có key riêng** *(quyết định `S-PATH`, 2026-09-17)*: `§layout` của module đã nói
|
|
23
|
+
> gốc ở đâu. Thêm một key nữa là **hai nguồn cho một sự thật**; khi chúng lệch, artifact ghi vào
|
|
24
|
+
> thư mục nền này còn script sinh theo layout nền kia — hỏng **im lặng**. Cùng lập luận đã dùng
|
|
25
|
+
> khi bỏ `tech_stack.qc_module`.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
automation/
|
|
29
|
+
├── web/ ← Playwright Test + TypeScript
|
|
30
|
+
│ ├── tests/{TICKET-ID}/{feature-slug}.spec.ts
|
|
31
|
+
│ ├── pages/{TICKET-ID}/{feature-slug}.page.ts
|
|
32
|
+
│ ├── fixtures/ ← client API/DB/Kafka dùng chung nhiều feature
|
|
33
|
+
│ ├── playwright/.auth/{role}.json ← storageState per-role (xem web-conventions.md §6.1)
|
|
34
|
+
│ └── playwright.config.ts
|
|
35
|
+
└── mobile/ ← WebdriverIO + Appium + TypeScript
|
|
36
|
+
├── test/specs/{TICKET-ID}/{feature-slug}.spec.ts
|
|
37
|
+
├── pageobjects/{TICKET-ID}/{feature-slug}.page.ts
|
|
38
|
+
├── fixtures/
|
|
39
|
+
└── wdio.conf.ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`{TICKET-ID}` — **dùng lại đúng 2 giá trị đã resolve ở Gate Bước 1** khi target là
|
|
43
|
+
UC-ID (cùng 2 segment đang dùng cho `{paths.specs_dir}/{TICKET-ID}/...`). Automation
|
|
44
|
+
code tổ chức theo **cấu trúc sản phẩm** (domain/feature-package), không theo UC-ID — vì 1 file
|
|
45
|
+
script thường phục vụ nhiều UC-ID cùng lúc (1 màn hình bị nhiều UC verify các khía cạnh khác
|
|
46
|
+
nhau), nên tổ chức theo UC-ID sẽ ép phải chọn 1 UC "sở hữu" file trong khi thực tế không đúng.
|
|
47
|
+
Đây là lý do automation code **KHÔNG** mirror cấu trúc `{paths.qc_dir}/{UC-ID}/...` (khác trục
|
|
48
|
+
tổ chức, không phải thiếu sót).
|
|
49
|
+
|
|
50
|
+
## 1. Chuyển đổi slug (bắt buộc, một chiều duy nhất)
|
|
51
|
+
|
|
52
|
+
Nguồn gốc của mọi slug là `<FEATURE>` trong tên file thiết kế manual `TC_<FEATURE>.md`
|
|
53
|
+
(qa-designer, UPPER_SNAKE_CASE, vd `TC_LOGIN.md`, `TC_STUDENT_ENROLLMENT.md`):
|
|
54
|
+
|
|
55
|
+
| Ngữ cảnh | Transform | Ví dụ (từ `TC_LOGIN.md`) |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| Tên file script/spec | kebab-case, giữ nguyên | `login.spec.ts` |
|
|
58
|
+
| Tên file Page Object | kebab-case + suffix `.page.ts` | `login.page.ts` |
|
|
59
|
+
| Tên class Page Object | PascalCase + suffix `Page` | `LoginPage` |
|
|
60
|
+
| Tag/tiêu đề test | giữ nguyên `TC_LOGIN_001` (từ TC ID gốc trong `.md`) | `test('TC_LOGIN_001 — ...', ...)` |
|
|
61
|
+
|
|
62
|
+
**Một chiều duy nhất** — không suy ngược từ code ra `<FEATURE>`, luôn suy từ `TC_<FEATURE>.md`
|
|
63
|
+
đã có sang code. Nếu chưa có TC file (chưa qua phase 3) thì chưa có gì để suy — không tự đặt
|
|
64
|
+
tên trước.
|
|
65
|
+
|
|
66
|
+
## 2. Nhiều UC cùng chạm 1 màn hình/feature (bắt buộc kiểm tra trước khi tạo file mới)
|
|
67
|
+
|
|
68
|
+
Feature/màn hình có thể được nhiều UC-ID khác nhau verify các khía cạnh khác nhau (vd UC1
|
|
69
|
+
"login thành công", UC2 "login khoá tài khoản" — cùng chạm màn Login). Trước khi tạo file mới
|
|
70
|
+
ở phase 6 (`qc-design-script`):
|
|
71
|
+
|
|
72
|
+
1. **Tìm Page Object đã tồn tại chưa** — glob
|
|
73
|
+
`automation/pages/{TICKET-ID}/*.page.ts` (hoặc mobile tương ứng)
|
|
74
|
+
theo cùng slug màn hình. Có rồi → tái sử dụng, **không tạo file PO thứ 2** cho cùng 1 màn.
|
|
75
|
+
2. **Tìm file spec đã tồn tại chưa** cùng slug — nếu UC khác đã tạo `login.spec.ts` cho cùng
|
|
76
|
+
màn hình, **thêm `test.describe()` mới vào file đó** (nhóm theo UC nguồn, vd
|
|
77
|
+
`test.describe('UC2 — Login lockout', () => {...})`), không tạo file trùng lặp
|
|
78
|
+
(`login-2.spec.ts`, `login-uc2.spec.ts`…).
|
|
79
|
+
3. Nếu `<FEATURE>` slug của 2 UC khác nhau nhưng cùng route/màn hình (do qa-designer đặt tên
|
|
80
|
+
khác nhau ở 2 lần chạy) → dùng slug của file **tạo trước** (kiểm tra tồn tại theo route,
|
|
81
|
+
không chỉ theo tên) để tránh phân mảnh; ghi chú lại trong report của `qc-design-script` nếu
|
|
82
|
+
phát hiện trường hợp này.
|
|
83
|
+
|
|
84
|
+
## 3. Ví dụ đầy đủ — UC1 (domain `academic`, prd-slug `student-enrollment`, screen Login)
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
automation/
|
|
88
|
+
├── tests/academic/student-enrollment/login.spec.ts # @trace.verifies=UC1-SC1, UC2-SC1...
|
|
89
|
+
├── pages/academic/student-enrollment/login.page.ts # class LoginPage
|
|
90
|
+
└── playwright/.auth/teacher.json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 4. Vị trí file "hồ sơ" khác (không phải code) — không đổi so với trước
|
|
94
|
+
|
|
95
|
+
`AUTOMATION_ASSESSMENT.md`, `TEST_DATA_PLAN.md`, `TESTABILITY_IMPROVEMENTS.md` vẫn ở
|
|
96
|
+
`{paths.qc_dir}/{UC-ID}/{active_platform}/` như các phase 4/5/6 đã định — đây là hồ sơ **theo
|
|
97
|
+
UC-ID** (đúng trục của chúng: đánh giá/dữ liệu/rào cản là quyết định gắn với 1 UC cụ thể tại 1
|
|
98
|
+
thời điểm), khác với code (gắn với màn hình, dùng lại xuyên UC). Hai trục tổ chức khác nhau
|
|
99
|
+
cho hai loại artifact khác nhau — không phải thiếu nhất quán.
|
|
100
|
+
|
|
101
|
+
> **Trạng thái tích hợp sdd-framework — ĐÃ THI HÀNH** *(Bước S · S1, 2026-09-17)*. Module của stack cũ
|
|
102
|
+
> — `qc-playwright` — đã **gỡ hẳn** *(quyết định `H2`)*, thay bằng hai module:
|
|
103
|
+
> `qc-playwright-ts` *(nền `web` và `system` — hai `layout`, cùng một dãy phiên bản Playwright/TS)*
|
|
104
|
+
> và `qc-wdio-appium` *(nền `app`)*. Không giữ bản deprecated nào: một hồ sơ stack cũ còn
|
|
105
|
+
> sống trong `core/` là một cửa để nó quay lại.
|
|
106
|
+
>
|
|
107
|
+
> Ghi chú gốc của bộ đề xuất từng dự kiến khai tên module qua `tech_stack.qc_module`. **Không
|
|
108
|
+
> làm vậy** — key đó chưa bao giờ tồn tại trong framework, và `active_platform` đã trả lời xong
|
|
109
|
+
> câu "dùng module nào" ngay ở trạm 1.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-09-09
|
|
4
|
+
new_in: QC-Workflow-Proposal (8-phase) — mobile chưa từng có trong skill set cũ
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Mobile Automation Conventions — TypeScript + WebdriverIO + Appium
|
|
8
|
+
|
|
9
|
+
Skill **tự chứa**: quy ước bắt buộc khi sinh automation script mobile bằng WebdriverIO +
|
|
10
|
+
Appium + TypeScript, cho app **Flutter** (dev team đang dùng). Mọi file layer trong
|
|
11
|
+
`qa-script-designer/mobile/*` đọc file này trước.
|
|
12
|
+
|
|
13
|
+
## 0. Vì sao Flutter cần driver riêng (đọc trước khi viết locator)
|
|
14
|
+
|
|
15
|
+
Flutter tự vẽ UI trên một canvas (Skia), **không** map trực tiếp sang native view tree như
|
|
16
|
+
React Native hay app native thật. Hệ quả: Appium driver chuẩn (`UiAutomator2` Android,
|
|
17
|
+
`XCUITest` iOS) chỉ "thấy" các widget mà Flutter **chủ động expose** qua `Semantics`
|
|
18
|
+
(accessibility tree của OS) — nếu widget không bật `Semantics`/không có `label`, driver chuẩn
|
|
19
|
+
coi như nó không tồn tại, dù mắt người thấy rõ trên màn hình.
|
|
20
|
+
|
|
21
|
+
**Vì vậy dùng `automationName: 'FlutterIntegration'`** (Appium Flutter Integration Driver,
|
|
22
|
+
dựa trên package `integration_test` của Flutter) — driver này đọc **thẳng Flutter widget
|
|
23
|
+
tree** qua `key` (`ValueKey`), không phụ thuộc accessibility tree của OS. Ổn định hơn nhiều
|
|
24
|
+
cho app Flutter, và đây là driver mặc định của mọi skill trong file này.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// wdio.conf.ts (trích)
|
|
28
|
+
export const config: WebdriverIO.Config = {
|
|
29
|
+
capabilities: [{
|
|
30
|
+
platformName: 'Android', // hoặc 'iOS'
|
|
31
|
+
'appium:automationName': 'FlutterIntegration',
|
|
32
|
+
'appium:app': process.env.APP_PATH,
|
|
33
|
+
}],
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 1. Locator Strategy — thứ tự ưu tiên (bắt buộc)
|
|
38
|
+
|
|
39
|
+
| # | Locator | Khi dùng | Ví dụ |
|
|
40
|
+
|---|---|---|---|
|
|
41
|
+
| 1 | **`ValueKey` contract (test-id Flutter)** | Luôn ưu tiên — tương đương `data-testid` bên web | `$('~login_submit_btn')` (FlutterIntegration map `~` → `ValueKey`) hoặc `driver.$('flutter finder', ...)` tuỳ version |
|
|
42
|
+
| 2 | **Semantics label / accessibility-id** | Widget có bật `Semantics(label: ...)` nhưng chưa gán `Key` | `$('~Đăng nhập')` |
|
|
43
|
+
| 3 | **Text hiển thị** (chỉ static, không lặp) | Không có key/label, element tĩnh duy nhất trên màn | `$('android=new UiSelector().text("Đăng nhập")')` |
|
|
44
|
+
| 4 | **XPath theo cấu trúc widget tree** (bất đắc dĩ) | Không còn lựa chọn nào khác | Luôn kèm `// FIXME: cần ValueKey, xem IMPROVE-xxx` |
|
|
45
|
+
|
|
46
|
+
**Không dùng làm mức đầu tiên:** toạ độ tuyệt đối (tap theo x/y — vỡ ngay khi đổi kích thước
|
|
47
|
+
màn hình/device), index không filter trên danh sách động, text đa ngôn ngữ dễ đổi.
|
|
48
|
+
|
|
49
|
+
### 1.1 Contract `ValueKey` với dev Flutter (bắt buộc xác nhận trước khi viết locator)
|
|
50
|
+
|
|
51
|
+
Tương đương `@trace.testid_attr` bên web — dev phải gán `Key(ValueKey('login_submit_btn'))`
|
|
52
|
+
cho mọi widget tương tác được liệt trong tech-doc §4.5.6. Đọc contract này trước khi viết
|
|
53
|
+
Page Object; thiếu → cảnh báo mềm, fallback Semantics label, và ghi `IMPROVE-xxx` đề nghị dev
|
|
54
|
+
bổ sung `ValueKey`.
|
|
55
|
+
|
|
56
|
+
### 1.2 Probe widget tree thật trước khi viết locator (bắt buộc nếu §4.5.6 không đủ)
|
|
57
|
+
|
|
58
|
+
Giống bên web phải "Probe DOM thật" khi SPA thiếu `data-testid`, bên mobile **không được đoán
|
|
59
|
+
`ValueKey`/label từ mắt nhìn màn hình** — phải xác nhận bằng công cụ trước khi viết locator:
|
|
60
|
+
|
|
61
|
+
- **Appium Inspector** (GUI chính thức của Appium): kết nối cùng `automationName:
|
|
62
|
+
FlutterIntegration` + capability của app đang test, tap vào widget để xem `ValueKey`/label
|
|
63
|
+
thật nó expose — không phải cấu trúc DOM/View suy đoán.
|
|
64
|
+
- **Flutter DevTools** (`flutter run` + mở DevTools → Widget Inspector): xem trực tiếp widget
|
|
65
|
+
tree đang chạy, kể cả khi widget chưa gán `Key` (giúp biết chính xác cần đề nghị dev thêm
|
|
66
|
+
`Key` ở widget nào, thay vì đoán qua ảnh chụp màn hình).
|
|
67
|
+
|
|
68
|
+
Không có 1 trong 2 công cụ trên khả dụng (vd môi trường không cài được) → fallback: build app
|
|
69
|
+
debug, chạy tay 1 lần, dựa vào Semantics label nếu bật `flutter run --enable-software-rendering
|
|
70
|
+
--dds-port` kèm accessibility bật trên OS — nhưng đây là **fallback chậm hơn**, ưu tiên 2 công
|
|
71
|
+
cụ trên trước.
|
|
72
|
+
|
|
73
|
+
## 2. Page Object Model — 3 lớp (bắt buộc, giống cấu trúc web)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// pageobjects/login.page.ts
|
|
77
|
+
import { BasePage } from './base.page';
|
|
78
|
+
|
|
79
|
+
class LoginPage extends BasePage {
|
|
80
|
+
// ── Lớp 1: Locators ──
|
|
81
|
+
private get emailInput() { return $('~login_email_input'); }
|
|
82
|
+
private get passwordInput() { return $('~login_password_input'); }
|
|
83
|
+
private get submitBtn() { return $('~login_submit_btn'); }
|
|
84
|
+
|
|
85
|
+
// ── Lớp 2: Actions ──
|
|
86
|
+
async login(email: string, password: string) {
|
|
87
|
+
await (await this.emailInput).setValue(email);
|
|
88
|
+
await (await this.passwordInput).setValue(password);
|
|
89
|
+
await (await this.submitBtn).click();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Lớp 3: Assertions ──
|
|
93
|
+
async assertValidationError(message: string) {
|
|
94
|
+
await expect($('~login_error')).toHaveText(message);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export default new LoginPage();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `BasePage` gọn: chứa helper chung (chờ app foreground, chụp screenshot).
|
|
101
|
+
- Điều hướng đa màn: mỗi màn 1 Page Object riêng, action trả về hoặc test tự import Page Object
|
|
102
|
+
màn kế tiếp (module singleton pattern WDIO chuẩn — khác Playwright PO là instance theo `page`).
|
|
103
|
+
|
|
104
|
+
## 3. Wait & Timing (bắt buộc)
|
|
105
|
+
|
|
106
|
+
- **Không** `browser.pause(N)` cố định — dùng `waitForDisplayed()`, `waitForEnabled()`,
|
|
107
|
+
`waitUntil()` của WebdriverIO (auto-retry theo điều kiện).
|
|
108
|
+
- App Flutter cần thời gian dựng frame đầu — dùng `waitForDisplayed({ timeout: 10000 })` cho
|
|
109
|
+
màn hình đầu sau cold start, không phải `pause`.
|
|
110
|
+
- Thao tác vuốt/scroll trên danh sách dài: dùng gesture helper WDIO (`touchAction`/
|
|
111
|
+
`$.scrollIntoView()`), không lặp `pause` giữa mỗi lần vuốt.
|
|
112
|
+
|
|
113
|
+
## 4. Assertion (bắt buộc)
|
|
114
|
+
|
|
115
|
+
- Dùng matcher `expect(element).toBeDisplayed()/toHaveText()/toBeEnabled()` (WDIO
|
|
116
|
+
`expect-webdriverio`) — auto-retry, thông báo lỗi rõ.
|
|
117
|
+
- Không `assert` trần trụi; mỗi test assert nội dung/state thật, không chỉ "app không crash".
|
|
118
|
+
|
|
119
|
+
## 5. Test Independence & Data
|
|
120
|
+
|
|
121
|
+
- Không hard-code device/app path/credential → biến môi trường (`process.env.*`) hoặc
|
|
122
|
+
`wdio.conf.ts` theo profile.
|
|
123
|
+
- Reset app state giữa test qua `driver.reset()` hoặc deep-link/API riêng — **không** dựa vào
|
|
124
|
+
thứ tự test để "dọn" state của nhau.
|
|
125
|
+
- Native permission dialog (camera, location, notification) — xử lý qua Appium
|
|
126
|
+
`mobile: acceptAlert` / capability tự-cấp quyền khi cài, không tap toạ độ mù.
|
|
127
|
+
|
|
128
|
+
### 5.1 `noReset` / `fullReset` — chọn đúng đánh đổi tốc độ vs. cách ly
|
|
129
|
+
|
|
130
|
+
Capability quyết định mức "sạch" của app giữa các phiên chạy — chọn sai làm suite chậm không
|
|
131
|
+
cần thiết **hoặc** flaky do rò rỉ state:
|
|
132
|
+
|
|
133
|
+
| Capability | Hành vi | Khi dùng |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `fullReset: true` | Gỡ cài đặt + cài lại app mỗi session | TC cần trạng thái **cài đặt lần đầu** thật (onboarding, permission dialog lần đầu) — chậm nhất, dùng tối thiểu |
|
|
136
|
+
| `noReset: false` (mặc định, không set `fullReset`) | Xoá app data giữa session nhưng **không** gỡ cài đặt | Mặc định hợp lý cho phần lớn TC — cân bằng tốc độ/cách ly |
|
|
137
|
+
| `noReset: true` | Giữ nguyên toàn bộ data/session giữa các lần chạy | Chỉ dùng cho test **đọc** (không side-effect) hoặc khi đã tự quản lý cleanup qua API riêng — nhanh nhất nhưng rủi ro rò rỉ state nếu dùng sai chỗ |
|
|
138
|
+
|
|
139
|
+
Nguyên tắc chọn: TC có side-effect (tạo/sửa/xoá data, đổi setting) → không dùng `noReset: true`
|
|
140
|
+
trừ khi có cleanup API riêng đáng tin; TC chỉ đọc/verify hiển thị → `noReset: true` để tăng tốc
|
|
141
|
+
đáng kể cho suite lớn.
|
|
142
|
+
|
|
143
|
+
### 5.2 Device Farm cho Compatibility testing
|
|
144
|
+
|
|
145
|
+
Emulator/simulator không thay thế được test trên **thiết bị thật đa dạng OS version/hãng máy**
|
|
146
|
+
— đặc biệt quan trọng cho Android (phân mảnh OS/màn hình lớn hơn iOS nhiều). Khi TC
|
|
147
|
+
compatibility yêu cầu phủ nhiều device/OS thật mà không có đủ máy vật lý, dùng device farm
|
|
148
|
+
(BrowserStack App Automate, Sauce Labs, hoặc Firebase Test Lab) — cấu hình `capabilities` trỏ
|
|
149
|
+
tới farm qua remote WebDriver URL thay vì local Appium server, code test không đổi. Ghi rõ
|
|
150
|
+
trong TC non-functional compatibility: farm nào, danh sách device/OS mục tiêu, vì đây là chi
|
|
151
|
+
phí (thường tính phí theo phút chạy) cần PM/lead duyệt trước khi đưa vào CI thường xuyên.
|
|
152
|
+
|
|
153
|
+
## 6. Naming & Structure
|
|
154
|
+
|
|
155
|
+
Vị trí file + quy tắc slug — xem **`_shared/file-naming-and-folders.md`** (nguồn chuẩn duy
|
|
156
|
+
nhất). Quy ước còn lại riêng cho code:
|
|
157
|
+
|
|
158
|
+
| Gì | Convention |
|
|
159
|
+
|---|---|
|
|
160
|
+
| Test title | `it('TC_xxx — <mô tả ngắn>', async () => {...})` — tag Priority nhúng trong title (xem §6.1, WDIO/Mocha không có `tag` option như Playwright) |
|
|
161
|
+
| Comment trace | `// @trace.verifies={UC-ID}-SC{N}` ngay trên mỗi `it(...)` |
|
|
162
|
+
|
|
163
|
+
### 6.1 Mapping `Priority` (TC) → tag (script) — bắt buộc, suy tự động
|
|
164
|
+
|
|
165
|
+
WebdriverIO chạy trên Mocha — không có cơ chế `tag` như Playwright Test, nên tag nhúng ngay
|
|
166
|
+
trong title và lọc bằng `--mochaOpts.grep`. Đọc field `Priority` (`P0`/`P1`/`P2`) từ
|
|
167
|
+
`TC_<FEATURE>.md` — **không tự đặt theo cảm tính**:
|
|
168
|
+
|
|
169
|
+
| Priority (TC) | Title convention | Lý do |
|
|
170
|
+
|:---:|---|---|
|
|
171
|
+
| `P0` | `it('TC_xxx @smoke @regression — <mô tả>', ...)` | Tính năng lõi — vào cả smoke lẫn regression |
|
|
172
|
+
| `P1`/`P2` | `it('TC_xxx @regression — <mô tả>', ...)` | Chỉ vào regression đầy đủ |
|
|
173
|
+
|
|
174
|
+
**Chạy riêng smoke suite** (xem `/qc-smoke-test`):
|
|
175
|
+
`npx wdio run wdio.conf.ts --mochaOpts.grep "@smoke"`.
|
|
176
|
+
Review script đối chiếu đúng mapping này — P0 thiếu `@smoke` trong title là lỗi 🟠.
|
|
177
|
+
|
|
178
|
+
## 7. Common Pitfalls — lỗi thường gặp cần tránh (checklist self-review)
|
|
179
|
+
|
|
180
|
+
| # | Lỗi | Tại sao sai | Sửa |
|
|
181
|
+
|---|---|---|---|
|
|
182
|
+
| 1 | `browser.pause(5000)` sau mọi action | Chậm, flaky theo tốc độ máy/device thật vs emulator | `waitForDisplayed()`/`waitUntil()` |
|
|
183
|
+
| 2 | Locator theo toạ độ (`tap({x: 100, y: 200})`) | Vỡ ngay khi đổi kích thước màn hình/device khác | `ValueKey`/Semantics label |
|
|
184
|
+
| 3 | Dùng driver `UiAutomator2`/`XCUITest` thuần cho app Flutter không bật Semantics | Driver "mù" phần lớn UI, locator not-found sai lệch thành "bug sản phẩm" | `automationName: FlutterIntegration` + `ValueKey` |
|
|
185
|
+
| 4 | Không reset app state giữa test | Test sau phụ thuộc state test trước — flaky khi chạy lẻ | `driver.reset()` hoặc API/deep-link reset riêng |
|
|
186
|
+
| 5 | Bỏ qua xử lý permission dialog native | Test treo chờ dialog vô thời hạn hoặc tap sai nút | Capability tự-cấp quyền, hoặc `mobile: acceptAlert` |
|
|
187
|
+
| 6 | Test giả định luôn chạy trên 1 kích thước màn hình | Vỡ trên device thật khác resolution/aspect ratio | Locator theo key, tránh toạ độ; test responsive riêng nếu cần |
|
|
188
|
+
| 7 | Không phân biệt cold start vs app đã mở sẵn | Timeout sai (cold start chậm hơn nhiều) | Timeout riêng cho bước launch đầu tiên |
|
|
189
|
+
| 8 | Gộp test Android + iOS chung 1 file không parametrize | Trùng lặp code, khó bảo trì khi 1 platform đổi hành vi | Cấu hình `capabilities` theo platform, share Page Object logic, tách config |
|
|
190
|
+
|
|
191
|
+
## 8. Compile & Lint (bắt buộc trước khi báo Human approve)
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npx tsc --noEmit # type-check toàn bộ
|
|
195
|
+
npx wdio run wdio.conf.ts --spec ... --dryRun # nếu runner hỗ trợ; nếu không, review danh sách it() thủ công = số TC Automatable=Y
|
|
196
|
+
```
|