@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.
Files changed (147) hide show
  1. package/bin/lint-trace.js +4 -4
  2. package/bin/qc-base-map.json +13 -11
  3. package/bin/self-check.js +497 -16
  4. package/bin/trace-schema.json +3226 -2656
  5. package/core/FRAMEWORK_VERSION +1 -1
  6. package/core/commands/amend-prd.md +7 -1
  7. package/core/commands/debug.md +8 -2
  8. package/core/commands/define-product.md +38 -1
  9. package/core/commands/dev-gen-test.md +9 -3
  10. package/core/commands/dev-run-test.md +8 -2
  11. package/core/commands/dev-smoke-test.md +7 -1
  12. package/core/commands/extend-prd.md +7 -1
  13. package/core/commands/fix-bug.md +11 -5
  14. package/core/commands/generate-architecture.md +9 -1
  15. package/core/commands/generate-bdd.md +45 -5
  16. package/core/commands/generate-code.md +43 -4
  17. package/core/commands/generate-design-spec.md +7 -1
  18. package/core/commands/generate-prd.md +9 -1
  19. package/core/commands/generate-spec-manifest.md +7 -1
  20. package/core/commands/generate-tech-docs.md +41 -1
  21. package/core/commands/learn.md +7 -1
  22. package/core/commands/map-testids.md +11 -5
  23. package/core/commands/propose-scenario.md +7 -1
  24. package/core/commands/qc-analyze.md +12 -6
  25. package/core/commands/qc-automation-assess.md +356 -0
  26. package/core/commands/qc-design-script.md +430 -0
  27. package/core/commands/qc-design-test.md +98 -20
  28. package/core/commands/qc-plan.md +9 -3
  29. package/core/commands/qc-report.md +92 -77
  30. package/core/commands/qc-review-script.md +342 -0
  31. package/core/commands/{qc-review.md → qc-review-testcase.md} +86 -54
  32. package/core/commands/qc-run-manualtest.md +401 -0
  33. package/core/commands/qc-run-script.md +421 -0
  34. package/core/commands/refine-prd.md +7 -1
  35. package/core/commands/report-bug.md +9 -3
  36. package/core/commands/review-code.md +9 -3
  37. package/core/commands/review-context.md +11 -3
  38. package/core/commands/review-tech-docs.md +11 -3
  39. package/core/commands/setup-ai-first.md +7 -1
  40. package/core/commands/validate-traces.md +10 -4
  41. package/core/modules/qc-playwright-ts/module.yaml +13 -0
  42. package/core/modules/qc-playwright-ts/stack-profile.yaml +99 -0
  43. package/core/modules/qc-wdio-appium/module.yaml +20 -0
  44. package/core/modules/qc-wdio-appium/stack-profile.yaml +107 -0
  45. package/core/rules/workflow.md +2 -2
  46. package/core/skills/qc/_shared/self-review-principles.md +2 -2
  47. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +1 -1
  48. package/core/skills/qc/qa-analyst/data-flow.md +1 -1
  49. package/core/skills/qc/qa-analyst/spec-issue-reporter.md +1 -1
  50. package/core/skills/qc/qa-automation-assess/matrix.md +123 -0
  51. package/core/skills/qc/qa-designer/e2e/journey.md +1 -1
  52. package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +1 -1
  53. package/core/skills/qc/{qa-runner → qa-designer}/exploratory/session.md +8 -2
  54. package/core/skills/qc/qa-designer/functional/api.md +2 -2
  55. package/core/skills/qc/qa-designer/functional/gui-feature.md +1 -1
  56. package/core/skills/qc/qa-designer/functional/gui-screen.md +1 -1
  57. package/core/skills/qc/qa-designer/functional/job.md +128 -0
  58. package/core/skills/qc/qa-designer/integration/api.md +2 -2
  59. package/core/skills/qc/qa-designer/integration/db.md +2 -2
  60. package/core/skills/qc/qa-designer/integration/gui.md +1 -1
  61. package/core/skills/qc/qa-designer/integration/{kafka.md → queue.md} +21 -5
  62. package/core/skills/qc/qa-designer/non-functional.md +1 -1
  63. package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +17 -0
  64. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +28 -6
  65. package/core/skills/qc/qa-reviewer/script/_shared/review-rules.md +121 -0
  66. package/core/skills/qc/qa-reviewer/script/api/auth.md +49 -0
  67. package/core/skills/qc/qa-reviewer/script/api/endpoint.md +89 -0
  68. package/core/skills/qc/qa-reviewer/script/api/security.md +46 -0
  69. package/core/skills/qc/qa-reviewer/script/exploratory.md +3 -3
  70. package/core/skills/qc/qa-reviewer/script/mobile/e2e.md +41 -0
  71. package/core/skills/qc/qa-reviewer/script/mobile/functional.md +90 -0
  72. package/core/skills/qc/qa-reviewer/script/mobile/integration.md +41 -0
  73. package/core/skills/qc/qa-reviewer/script/mobile/non-functional.md +43 -0
  74. package/core/skills/qc/qa-reviewer/script/web/e2e.md +46 -0
  75. package/core/skills/qc/qa-reviewer/script/web/functional.md +111 -0
  76. package/core/skills/qc/qa-reviewer/script/web/integration.md +46 -0
  77. package/core/skills/qc/qa-reviewer/script/web/non-functional.md +49 -0
  78. package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +1 -1
  79. package/core/skills/qc/qa-reviewer/shared/review-file-template.md +29 -10
  80. package/core/skills/qc/qa-reviewer/test-case/e2e.md +2 -2
  81. package/core/skills/qc/qa-reviewer/test-case/exploratory.md +1 -1
  82. package/core/skills/qc/qa-reviewer/test-case/functional.md +2 -2
  83. package/core/skills/qc/qa-reviewer/test-case/integration.md +2 -2
  84. package/core/skills/qc/qa-reviewer/test-case/non-functional.md +2 -2
  85. package/core/skills/qc/qa-script-designer/_shared/api-conventions.md +94 -0
  86. package/core/skills/qc/qa-script-designer/_shared/file-naming-and-folders.md +109 -0
  87. package/core/skills/qc/qa-script-designer/_shared/mobile-conventions.md +196 -0
  88. package/core/skills/qc/qa-script-designer/_shared/web-conventions.md +257 -0
  89. package/core/skills/qc/qa-script-designer/api/auth.md +43 -0
  90. package/core/skills/qc/qa-script-designer/api/endpoint.md +61 -0
  91. package/core/skills/qc/qa-script-designer/api/security.md +41 -0
  92. package/core/skills/qc/qa-script-designer/mobile/e2e.md +35 -0
  93. package/core/skills/qc/qa-script-designer/mobile/functional/feature.md +32 -0
  94. package/core/skills/qc/qa-script-designer/mobile/functional/screen.md +42 -0
  95. package/core/skills/qc/qa-script-designer/mobile/integration.md +39 -0
  96. package/core/skills/qc/qa-script-designer/mobile/non-functional.md +39 -0
  97. package/core/skills/qc/qa-script-designer/web/e2e.md +36 -0
  98. package/core/skills/qc/qa-script-designer/web/functional/api.md +39 -0
  99. package/core/skills/qc/qa-script-designer/web/functional/gui-feature.md +34 -0
  100. package/core/skills/qc/qa-script-designer/web/functional/gui-screen.md +42 -0
  101. package/core/skills/qc/qa-script-designer/web/integration.md +43 -0
  102. package/core/skills/qc/qa-script-designer/web/non-functional.md +42 -0
  103. package/core/skills/qc/qa-script-runner/mobile/run.md +38 -0
  104. package/core/skills/qc/qa-script-runner/report.md +41 -0
  105. package/core/skills/qc/qa-script-runner/web/run.md +48 -0
  106. package/core/steps/context-loader.md +1 -1
  107. package/core/steps/gate.md +7 -1
  108. package/core/steps/qc-scope.md +45 -2
  109. package/core/steps/qc-stamp.md +4 -4
  110. package/core/steps/report-footer.md +10 -9
  111. package/docs/02-concepts/pipeline-steps/07-dev-selftest.md +1 -1
  112. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +13 -12
  113. package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +3 -3
  114. package/docs/02-concepts/traceability.md +1 -1
  115. package/docs/03-guides/developer.md +1 -1
  116. package/docs/03-guides/tester-qa.md +40 -11
  117. package/docs/04-reference/commands.md +4 -2
  118. package/docs/04-reference/modules.md +2 -1
  119. package/docs/04-reference/trace-schema.md +4 -4
  120. package/docs/explain/17-qc-design-test.md +5 -5
  121. package/docs/explain/18-qc-review.md +42 -20
  122. package/docs/explain/19-qc-run-test.md +13 -10
  123. package/docs/explain/20-qc-report.md +3 -3
  124. package/docs/explain/23-fix-bug.md +2 -2
  125. package/docs/explain/README.md +2 -2
  126. package/docs/plans/qc-surgery/01-checklist.md +86 -21
  127. package/docs/plans/qc-surgery/PLAN_v2.md +295 -0
  128. package/docs/plans/qc-surgery/exec-S-ap-stack-typescript.md +420 -0
  129. package/docs/plans/qc-surgery/exec-S0-guard-cam-stack-cu.md +400 -0
  130. package/docs/plans/qc-surgery/exec-S1-hai-module-thay-qc-playwright.md +267 -0
  131. package/docs/plans/qc-surgery/exec-S2-qa-runner-thanh-script-designer-runner.md +340 -0
  132. package/docs/plans/qc-surgery/exec-S3-viet-lai-tieu-chi-review-script.md +322 -0
  133. package/docs/plans/qc-surgery/exec-S5-an-theo-don-dau-vet-stack-cu.md +292 -0
  134. package/package.json +1 -1
  135. package/core/commands/qc-run-test.md +0 -561
  136. package/core/modules/qc-playwright/stack-profile.yaml +0 -66
  137. package/core/skills/qc/qa-reviewer/script/e2e.md +0 -95
  138. package/core/skills/qc/qa-reviewer/script/functional.md +0 -109
  139. package/core/skills/qc/qa-reviewer/script/integration.md +0 -99
  140. package/core/skills/qc/qa-reviewer/script/non-functional.md +0 -134
  141. package/core/skills/qc/qa-runner/e2e.md +0 -49
  142. package/core/skills/qc/qa-runner/functional/api.md +0 -35
  143. package/core/skills/qc/qa-runner/functional/gui-feature.md +0 -57
  144. package/core/skills/qc/qa-runner/functional/gui-screen.md +0 -61
  145. package/core/skills/qc/qa-runner/integration.md +0 -47
  146. package/core/skills/qc/qa-runner/non-functional.md +0 -49
  147. package/core/skills/qc/qa-runner/report/report.md +0 -37
@@ -0,0 +1,257 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-09
4
+ source: upstream/qc-base-new/Automation-Standards.md §4 §6 §7 §8 §9 §10 §11 (Approved)
5
+ ---
6
+
7
+ # Web Automation Conventions — TypeScript + Playwright Test
8
+
9
+ Skill **tự chứa**: quy ước bắt buộc khi sinh automation script web bằng `@playwright/test` +
10
+ TypeScript. Mọi file layer trong `qa-script-designer/web/*` đọc file này trước — không lặp
11
+ lại các quy tắc ở đây, chỉ có phần logic sinh-script riêng của layer.
12
+
13
+ ## 1. Locator Strategy — thứ tự ưu tiên (bắt buộc)
14
+
15
+ Đây là nguồn lỗi automation phổ biến nhất: locator giòn (fragile) làm script fail không phải
16
+ vì bug sản phẩm mà vì UI đổi CSS/text/thứ tự DOM. Luôn theo đúng thứ tự ưu tiên sau, **dừng ở
17
+ mức đầu tiên khả dụng** — không nhảy xuống mức thấp hơn "cho nhanh":
18
+
19
+ | # | Locator | Khi dùng | Ví dụ |
20
+ |---|---|---|---|
21
+ | 1 | **Test-ID contract** | Luôn ưu tiên nếu tech-doc có `@trace.testid_attr` + bảng §4.5.6 | `page.getByTestId('login-submit-btn')` |
22
+ | 2 | **Role + Accessible Name** | Không có test-id nhưng element có role/aria chuẩn (button/link/textbox/checkbox…) | `page.getByRole('button', { name: 'Đăng nhập' })` |
23
+ | 3 | **Label** (cho form field) | Input có `<label for>` hoặc `aria-label` | `page.getByLabel('Email')` |
24
+ | 4 | **Text** (chỉ cho element tĩnh, không lặp lại nhiều lần trên trang) | Heading, thông báo, tiêu đề duy nhất | `page.getByText('Đăng nhập thành công')` |
25
+ | 5 | **CSS/XPath theo cấu trúc ổn định** (BEM class, không phải class utility sinh tự động) | Bất khả kháng — không có gì ở trên | `page.locator('.login-form__submit')` |
26
+
27
+ **Không bao giờ dùng làm mức đầu tiên** (chỉ là fallback bất đắc dĩ, luôn kèm ghi chú
28
+ `// FIXME: cần test-id, xem IMPROVE-xxx`):
29
+ - Class do bundler/CSS-in-JS sinh tự động (`css-1a2b3c`, `sc-hGRVom`, các hash động) — đổi mỗi
30
+ lần build, **không ổn định giữa các lần deploy**.
31
+ - XPath tuyệt đối/theo vị trí (`/html/body/div[3]/div[2]/button`) — vỡ ngay khi DOM chèn thêm 1
32
+ node.
33
+ - Index không có điều kiện đi kèm (`.first()`, `nth(2)`) khi danh sách có thể thay đổi thứ tự.
34
+ - Text tiếng Việt cho element có thể đổi copy/đa ngôn ngữ.
35
+
36
+ **Locator cho danh sách/bảng động:** dùng `locator().filter({ hasText })` hoặc lọc theo
37
+ test-id của row thay vì đếm index cố định; nếu cần verify thứ tự thật (sort/paging), so sánh
38
+ mảng giá trị thay vì giả định vị trí.
39
+
40
+ ### 1.1 Cấu hình test-id attribute (bắt buộc trước khi dùng `getByTestId`)
41
+
42
+ Đọc `@trace.testid_attr` từ header tech-doc gộp:
43
+
44
+ | Đọc được | Làm gì |
45
+ |---|---|
46
+ | `data-testid` (mặc định Playwright) | Dùng thẳng `getByTestId()`. |
47
+ | Khác `data-testid` (vd `data-test`, `data-qa`) | **Bắt buộc** cấu hình trước: `playwright.config.ts` → `use: { testIdAttribute: 'data-test' }`. Bỏ bước này → `getByTestId()` luôn tìm `data-testid` mặc định → **locator trượt 100%**, trông giống bug sản phẩm nhưng thực ra là script-bug. |
48
+ | Không có field | Cảnh báo mềm, fallback Role/Text, và ghi `IMPROVE-xxx` đề nghị dev bổ sung field. |
49
+
50
+ ## 2. Page Object Model — 3 lớp (bắt buộc)
51
+
52
+ ```typescript
53
+ // pages/login.page.ts
54
+ import { type Page, type Locator, expect } from '@playwright/test';
55
+ import { BasePage } from './base.page';
56
+
57
+ export class LoginPage extends BasePage {
58
+ // ── Lớp 1: Locators — chỉ khai báo, KHÔNG hành động ──
59
+ private readonly emailInput: Locator;
60
+ private readonly passwordInput: Locator;
61
+ private readonly submitBtn: Locator;
62
+
63
+ constructor(page: Page) {
64
+ super(page);
65
+ this.emailInput = page.getByTestId('login-email-input');
66
+ this.passwordInput = page.getByTestId('login-password-input');
67
+ this.submitBtn = page.getByTestId('login-submit-btn');
68
+ }
69
+
70
+ // ── Lớp 2: Actions — verb_noun(), trả về Page kế tiếp nếu điều hướng ──
71
+ async login(email: string, password: string): Promise<DashboardPage> {
72
+ await this.emailInput.fill(email);
73
+ await this.passwordInput.fill(password);
74
+ await this.submitBtn.click();
75
+ return new DashboardPage(this.page);
76
+ }
77
+
78
+ // ── Lớp 3: Assertions — expect(), không bare assert ──
79
+ async assertValidationError(message: string): Promise<void> {
80
+ await expect(this.page.getByTestId('login-error')).toHaveText(message);
81
+ }
82
+ }
83
+ ```
84
+
85
+ - Kế thừa `BasePage` gọn (chứa `page`, helper chung như `screenshot()`), **không** framework
86
+ report riêng (Allure…) — reporter là của Playwright Test.
87
+ - Điều hướng giữa màn: action trả về Page Object màn kế tiếp (`return new NextPage(this.page)`)
88
+ — test chuỗi được `const dashboard = await loginPage.login(...)`.
89
+ - Selector khai báo tập trung ở constructor/đầu class — không rải trong action method.
90
+ - Test **không bao giờ** gọi `page.click()`/`page.fill()` trực tiếp — luôn qua method của PO.
91
+
92
+ ## 3. Report Readability — `test.step()` (bắt buộc)
93
+
94
+ Playwright Test có `test.step()` built-in — report/trace viewer hiển thị từng step rõ ràng,
95
+ thu gọn được, giúp đọc lại 1 test dài dễ hơn nhiều so với đọc thẳng code:
96
+
97
+ ```typescript
98
+ test('TC_LOGIN_001 — đăng nhập thành công', async ({ page }) => {
99
+ const loginPage = new LoginPage(page);
100
+
101
+ await test.step('Nhập thông tin đăng nhập hợp lệ', async () => {
102
+ await loginPage.login('user@test.com', 'Password123');
103
+ });
104
+
105
+ await test.step('Xác nhận vào được Dashboard', async () => {
106
+ await expect(page).toHaveURL(/\/dashboard/);
107
+ });
108
+ });
109
+ ```
110
+
111
+ Bọc **mỗi chặng có ý nghĩa nghiệp vụ** (không phải mỗi dòng code) bằng `test.step()` — đặc
112
+ biệt quan trọng cho test đa bước (feature đa-màn, E2E journey) nơi report cần chỉ đúng bước
113
+ nào fail thay vì chỉ báo cả `test()` fail chung chung.
114
+
115
+ ## 4. Wait & Timing (bắt buộc)
116
+
117
+ - **Không bao giờ** `page.waitForTimeout(N)` cố định để "chờ cho chắc" — dùng auto-wait của
118
+ `expect()`/action locator (Playwright tự retry tới khi actionable hoặc timeout).
119
+ - Sau navigation/submit cần chờ mạng ổn định: `page.waitForLoadState('networkidle')` — nhưng
120
+ **không** gọi `waitForTimeout` ngay sau đó (thừa, che giấu race condition thật).
121
+ networkidle không tương thích tốt hoặc app poll liên tục.
122
+ - Verify network call thật: `page.waitForResponse(url => ...)` — không mock trong integration
123
+ test (mock = unit test, không phải test tích hợp).
124
+
125
+ ## 5. Assertion (bắt buộc)
126
+
127
+ - Dùng `expect(locator).toHaveText/toBeVisible/toHaveValue(...)` — auto-retry, thông báo lỗi rõ.
128
+ - **Không** `assert someCondition` trần trụi không thông điệp; **không** `assert response` mơ hồ.
129
+ - Mỗi test **assert nội dung thật**, không chỉ assert URL sau điều hướng.
130
+ - Negative case: assert đúng thông báo lỗi cụ thể, không chỉ "có lỗi xuất hiện".
131
+ - **`expect.soft()`** khi cần assert nhiều điều độc lập trong 1 test mà không muốn dừng ngay ở
132
+ assert đầu tiên fail (vd: kiểm tra đồng thời 5 field hiển thị đúng trên 1 form) — Playwright
133
+ gom hết soft-assertion fail rồi báo 1 lần cuối test, đọc report đỡ mất công chạy lại từng cái
134
+ một. **Không lạm dụng**: assertion mang tính điều kiện tiên quyết cho bước sau (vd "đã login
135
+ chưa") vẫn phải dùng `expect()` thường để dừng ngay, tránh test tiếp tục chạy trên state sai.
136
+
137
+ ## 6. Test Independence & Data
138
+
139
+ - Không hard-code URL/credential/timeout → `process.env.*` hoặc file `config/env.ts`.
140
+ - Mỗi test độc lập: dữ liệu từ `TEST_DATA_PLAN.md` (factory/fixture), cleanup sau khi tạo.
141
+ - Không global state chia sẻ giữa test; gom test theo (role, account) nếu auth tốn chi phí,
142
+ qua Playwright `test.describe.serial` có chủ đích — không phải mặc định.
143
+
144
+ ### 6.1 Auth reuse — `storageState` (bắt buộc khi feature cần login)
145
+
146
+ Login qua UI ở **mỗi** test (điền form, submit, chờ redirect) là nguồn chậm + flaky lớn nhất
147
+ của một suite Playwright — mỗi lần login là một chuỗi network call thật. Best practice chuẩn
148
+ của Playwright: **login một lần trong global setup, lưu lại session, mọi test tái sử dụng**:
149
+
150
+ ```typescript
151
+ // auth.setup.ts — chạy 1 lần trước cả suite (khai trong playwright.config.ts: projects: [{ name: 'setup', testMatch: /auth\.setup\.ts/ }])
152
+ import { test as setup } from '@playwright/test';
153
+
154
+ setup('authenticate as teacher', async ({ page }) => {
155
+ await page.goto('/login');
156
+ await page.getByTestId('login-email-input').fill(process.env.TEACHER_EMAIL!);
157
+ await page.getByTestId('login-password-input').fill(process.env.TEACHER_PASSWORD!);
158
+ await page.getByTestId('login-submit-btn').click();
159
+ await page.waitForURL(/\/dashboard/);
160
+ await page.context().storageState({ path: 'playwright/.auth/teacher.json' });
161
+ });
162
+ ```
163
+
164
+ ```typescript
165
+ // playwright.config.ts (trích) — project phụ thuộc setup, dùng lại storageState
166
+ projects: [
167
+ { name: 'setup', testMatch: /auth\.setup\.ts/ },
168
+ { name: 'teacher-tests', use: { storageState: 'playwright/.auth/teacher.json' }, dependencies: ['setup'] },
169
+ ],
170
+ ```
171
+
172
+ - Mỗi role cần 1 file storageState riêng (`teacher.json`, `admin.json`…) — khớp `Account` trong
173
+ `TEST_DATA_PLAN.md`.
174
+ - TC **về chính hành vi login** (validate lỗi sai mật khẩu, khoá tài khoản…) **không** dùng
175
+ storageState — đó là TC test luôn chính flow login qua UI, không phải TC cần login xong mới
176
+ test cái khác.
177
+ - File storageState chứa session thật → thêm vào `.gitignore`, không commit.
178
+
179
+ ## 7. Retry & Flaky Quarantine (bắt buộc cấu hình)
180
+
181
+ Test automation **luôn** có một tỷ lệ flaky nền do timing/network/môi trường CI — coi đó là
182
+ bình thường và có quy trình xử lý có hệ thống, thay vì coi mọi lần fail là product-gap hoặc
183
+ mọi lần pass-sau-retry là "không sao":
184
+
185
+ ```typescript
186
+ // playwright.config.ts (trích)
187
+ export default defineConfig({
188
+ retries: process.env.CI ? 2 : 0, // CI retry tối đa 2 lần; local KHÔNG retry (fail phải hiện ngay để debug)
189
+ reporter: [['html'], ['list']],
190
+ });
191
+ ```
192
+
193
+ - **Retry chỉ để hấp thụ nhiễu môi trường CI, không phải để che giấu bug.** Test pass sau
194
+ retry vẫn hiện trong report là "flaky" (Playwright HTML Report tự đánh dấu `flaky` khác với
195
+ `passed`) — **không coi là pass sạch**. Đây là tín hiệu `qc-run-script` phải đọc để đưa vào
196
+ nhóm cần điều tra riêng (xem gate phân loại Fail của `/qc-run-script`), không tự động lờ đi.
197
+ - **Quarantine**: một test flaky lặp lại ≥3 lần chạy gần nhất → tách khỏi suite chính
198
+ (`test.skip` tạm + tag `@flaky`, hoặc project riêng `flaky-tests` không chặn CI), mở
199
+ `IMPROVE-xxx`/ghi chú kỹ thuật để điều tra root cause (thường là thiếu `storageState`, thiếu
200
+ cách ly data, hoặc race condition thật trong sản phẩm) — không để một test flaky nằm mãi
201
+ trong suite chính làm nhiễu tín hiệu của tất cả lần chạy sau.
202
+
203
+ ## 8. Naming & Structure
204
+
205
+ Vị trí file + quy tắc slug (`<feature>` từ `TC_<FEATURE>.md`, kiểm tra tồn tại trước khi tạo
206
+ mới khi nhiều UC cùng chạm 1 màn) — xem **`_shared/file-naming-and-folders.md`** (nguồn chuẩn
207
+ duy nhất, không lặp lại bảng path ở đây nữa). Quy ước còn lại riêng cho code:
208
+
209
+ | Gì | Convention |
210
+ |---|---|
211
+ | Test title | `test('TC_xxx — <mô tả ngắn>', async ({ page }) => {...})` — TC_ID **trong title**, không chỉ comment |
212
+ | Tag | Suy **tự động** từ `Priority` của TC gốc — xem §8.1, không tự chọn tuỳ ý |
213
+ | Comment trace | `// @trace.verifies={UC-ID}-SC{N}` ngay trên mỗi `test(...)` |
214
+
215
+ ### 8.1 Mapping `Priority` (TC) → tag (script) — bắt buộc, suy tự động
216
+
217
+ `qc-design-script` đọc field `Priority` (`P0`/`P1`/`P2`) đã có sẵn trong `TC_<FEATURE>.md` —
218
+ **không tự đặt tag theo cảm tính**:
219
+
220
+ | Priority (TC) | Tag gắn vào `test()` | Lý do |
221
+ |:---:|---|---|
222
+ | `P0` | `{ tag: ['@smoke', '@regression'] }` | P0 = tính năng lõi/luồng chính — vừa chạy trong smoke suite (nhanh, mỗi build) vừa nằm trong regression đầy đủ |
223
+ | `P1` | `{ tag: ['@regression'] }` | Quan trọng nhưng không phải lõi — không cần chặn mỗi build, chạy ở regression đầy đủ |
224
+ | `P2` | `{ tag: ['@regression'] }` | Biên/phụ — cùng regression, không vào smoke |
225
+
226
+ ```typescript
227
+ test('TC_LOGIN_001 — đăng nhập thành công', { tag: ['@smoke', '@regression'] }, async ({ page }) => {
228
+ // ...
229
+ });
230
+ ```
231
+
232
+ **Chạy riêng smoke suite** (xem `/qc-smoke-test`): `npx playwright test --grep @smoke`.
233
+ Review script (`/qc-review`, `qa-reviewer/script/web/*`) đối chiếu đúng mapping này — tag lệch
234
+ Priority (vd P0 nhưng thiếu `@smoke`) là lỗi 🟠 cần sửa, vì nó khiến smoke suite bỏ sót tính
235
+ năng lõi.
236
+
237
+ ## 9. Common Pitfalls — lỗi thường gặp cần tránh (checklist self-review)
238
+
239
+ | # | Lỗi | Tại sao sai | Sửa |
240
+ |---|---|---|---|
241
+ | 1 | `await page.waitForTimeout(3000)` để "chờ load" | Chậm, flaky (mạng chậm hơn 3s vẫn fail; nhanh hơn thì lãng phí) | `expect(locator).toBeVisible()` / `waitForLoadState` |
242
+ | 2 | Locator theo class hash (`'.css-1x2y3z'`) | Vỡ mỗi lần build lại CSS-in-JS | Test-id contract, hoặc Role/Label |
243
+ | 3 | `test.skip()` không lý do | Không biết TC bị bỏ vì gì, dễ quên xử lý | `test.skip(true, 'GAP-03 — chưa có API reset state')` |
244
+ | 4 | Assertion chỉ ở bước cuối cùng của journey dài | Bug ở giữa flow bị che bởi state cuối đúng "tình cờ" | Assert sau mỗi bước có side-effect quan trọng |
245
+ | 5 | Test phụ thuộc thứ tự chạy (`test B` cần `test A` chạy trước) | Playwright chạy song song mặc định — sinh flaky ngẫu nhiên | Fixture độc lập cho mỗi test, không biến global |
246
+ | 6 | Hard-code data đã tồn tại sẵn trên môi trường (`userId: 42`) | Vỡ khi seed data đổi, không chạy song song được | Tạo qua API/fixture trong `beforeEach`, cleanup `afterEach` |
247
+ | 7 | Bắt exception rồi bỏ qua (`try { ... } catch {}`) để test "luôn xanh" | Che giấu lỗi thật, fake-pass | Không catch; để test fail đúng bản chất |
248
+ | 8 | Dùng `.first()`/`.nth(N)` không kèm filter | Thứ tự phần tử có thể đổi (server-side sort, feature flag) | `.filter({ hasText })`, hoặc locator theo test-id của item |
249
+ | 9 | Test gọi trực tiếp `page.locator(...)` thay vì qua Page Object | Trùng lặp selector nhiều nơi, khó bảo trì khi UI đổi | Mọi interaction qua method PO |
250
+ | 10 | So sánh screenshot toàn trang cho mọi test (visual regression tràn lan) | Flaky theo font/OS/timing render, chi phí review cao | Chỉ dùng visual assertion cho component cụ thể cần, có mask vùng động |
251
+
252
+ ## 10. Compile & Lint (bắt buộc trước khi báo Human approve)
253
+
254
+ ```bash
255
+ npx tsc --noEmit # type-check toàn bộ, không build ra file
256
+ npx playwright test --list # liệt kê test sẽ chạy — đếm phải khớp số TC Automatable=Y
257
+ ```
@@ -0,0 +1,43 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-17
4
+ source: upstream/qc-base-new/API-Testing-Standards.md §5.5 §9.1 · AGT-010 (Approved)
5
+ ---
6
+
7
+ # Sinh script — API xác thực & phân quyền
8
+
9
+ **Đọc `_shared/api-conventions.md` rồi `endpoint.md` trước.** Đây là phần thêm.
10
+
11
+ ## Khi nào trigger
12
+ - TC kiểm đăng nhập, vòng đời token, hoặc **quyền truy cập theo vai** lên endpoint.
13
+
14
+ ## Phase 1 — Clarify
15
+ Liệt kê **mọi endpoint được bảo vệ** trong phạm vi, và **mọi vai** có trong hợp đồng. Bảng
16
+ vai × endpoint là đầu vào của Phase 2.
17
+
18
+ ## Phase 2 — Generate
19
+
20
+ **Fixture token theo vai** *(§5.5)* — `fixtures/api.fixture.ts` cấp một context cho mỗi vai.
21
+ Không đăng nhập lại trong từng test: chậm, và dễ chạm rate-limit.
22
+
23
+ **Mỗi endpoint được bảo vệ sinh đủ ba ca** *(§9.1)*:
24
+
25
+ | Ca | Mong đợi |
26
+ |---|---|
27
+ | Không token | `401` |
28
+ | Token sai / hết hạn | `401` |
29
+ | Token đúng nhưng **sai vai** | `403` |
30
+
31
+ Không gộp `401` với `403` — hai lỗi khác nhau: *chưa biết anh là ai* vs *biết rồi nhưng anh
32
+ không được phép*. Gộp là mất khả năng phân biệt lỗi cấu hình xác thực với lỗi phân quyền.
33
+
34
+ **Assert phải là "bị chặn", không phải "không lỗi":** assert đúng mã, **và** assert body không
35
+ rò dữ liệu của tài nguyên bị cấm.
36
+
37
+ ## Phase 3 — Trace tag + cột `Script file`
38
+ Như `endpoint.md` Phase 3 và Phase 4, `@trace.test_type=functional`.
39
+
40
+ ## Self-review
41
+ - Mỗi endpoint được bảo vệ có đủ 3 ca?
42
+ - Fixture phân biệt **theo vai**, không dùng một token cho mọi test?
43
+ - Không token thật nào nằm trong code?
@@ -0,0 +1,61 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-17
4
+ source: upstream/qc-base-new/API-Testing-Standards.md §4 §5 §6 §8 · AGT-010 (Approved)
5
+ ---
6
+
7
+ # Sinh script — API endpoint *(CRUD, mã trạng thái, cấu trúc response)*
8
+
9
+ **Đọc `_shared/api-conventions.md` trước.** File này chỉ có phần sinh-script riêng của tầng
10
+ endpoint. Lane này dùng khi `active_platform = system`.
11
+
12
+ ## Khi nào trigger
13
+ - Chuyển TC `Automatable: Y` của một endpoint *(từ `AUTOMATION_ASSESSMENT.md`)* thành spec,
14
+ sau khi `TC_<FEATURE>.Test.md` đã qua `/qc-review-testcase`.
15
+
16
+ ## Khi KHÔNG trigger
17
+ - Xác thực / phân quyền → `auth.md` · biên đầu vào & injection → `security.md`
18
+ - API gọi phụ trợ **bên trong** dự án web *(setup/teardown)* → `web/functional/api.md`
19
+
20
+ ## Phase 1 — Clarify
21
+
22
+ Đọc `TC_<FEATURE>.Test.md` **chỉ các TC `Automatable: Y`** + `TEST_DATA_PLAN.md`. API Object cho
23
+ resource này đã có chưa → tạo mới nếu chưa. Đọc hợp đồng endpoint từ tech-doc: path · method ·
24
+ payload · mã trạng thái · cấu trúc response.
25
+
26
+ ## Phase 2 — Generate
27
+
28
+ **Phủ đúng số TC `Automatable: Y`** — đếm trong `AUTOMATION_ASSESSMENT.md` = số `test(...)` phải
29
+ sinh, **không hơn không kém**. TC `Y` mà gặp rào cản kỹ thuật lúc viết → `test.fixme('TC-API-xxx — {lý do}')`
30
+ + ghi `IMPROVE-xxx`, **không âm thầm bỏ qua**.
31
+
32
+ Gom bằng `test.describe` theo **resource → method**. Mỗi TC một `test()`, tiêu đề mang `TC-API-ID`.
33
+
34
+ Sinh đủ ba lớp:
35
+
36
+ | Lớp | File |
37
+ |---|---|
38
+ | API Object | `api-automation/api/<resource>.api.ts` — 1 method = 1 endpoint action |
39
+ | Test data | `api-automation/data/<resource>.data.ts` |
40
+ | Spec | `api-automation/tests/{TICKET-ID}/<feature>-<scenario>.spec.ts` |
41
+
42
+ ## Phase 3 — Trace tag bắt buộc
43
+
44
+ ```typescript
45
+ // @trace.verifies={UC-ID}-SC{N}
46
+ // @trace.source=specs/{domain}/{prd-slug}/bdd/system/{UC-ID}-{slug}.feature
47
+ // @trace.test_type=functional
48
+ ```
49
+
50
+ ## Phase 4 — Điền cột `Script file`
51
+
52
+ Sau khi sinh, **điền đường dẫn thật** vào cột `Script file` của từng TC `Y` trong
53
+ `AUTOMATION_ASSESSMENT.md`. Đây là chỉ mục ngược duy nhất TC → file code; `/qc-run-script` đọc
54
+ đúng cột này để biết chạy file nào.
55
+
56
+ ## Self-review trước khi báo xong
57
+
58
+ - Số `test()` = số TC `Automatable: Y`?
59
+ - Mọi request đi qua API Object, **không** `request.*` thô trong spec?
60
+ - Mọi test assert **mã trạng thái** + ít nhất một assert về nội dung?
61
+ - `npx tsc --noEmit` sạch và `--list` collect đúng số?
@@ -0,0 +1,41 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-17
4
+ source: upstream/qc-base-new/API-Testing-Standards.md §8 §9.2 · AGT-010 (Approved)
5
+ ---
6
+
7
+ # Sinh script — API biên đầu vào & injection
8
+
9
+ **Đọc `_shared/api-conventions.md` rồi `endpoint.md` trước.** Đây là phần thêm.
10
+
11
+ ## Khi nào trigger
12
+ - TC kiểm **biên của đầu vào** hoặc các mẫu injection lên endpoint.
13
+
14
+ ## Phase 1 — Clarify
15
+ Với mỗi trường đầu vào, lấy từ hợp đồng: kiểu · bắt buộc/không · giới hạn min–max · tập giá trị
16
+ hợp lệ. Thiếu ràng buộc trong hợp đồng → ghi `IMPROVE-xxx`, **không tự bịa ngưỡng**.
17
+
18
+ ## Phase 2 — Generate
19
+
20
+ **Ba kỹ thuật phải thấy được trong code** *(§8)*:
21
+
22
+ | Kỹ thuật | Sinh gì |
23
+ |---|---|
24
+ | **Phân lớp tương đương** | mỗi lớp ít nhất một ca — không chỉ ca hợp lệ |
25
+ | **Giá trị biên** | với trường có giới hạn: `min-1` · `min` · `max` · `max+1` |
26
+ | **Phủ HTTP method** | gọi method không hỗ trợ → mong đợi `405` |
27
+
28
+ **Payload injection** *(§9.2)* — gom vào `data/<resource>.data.ts` thành **một bộ dùng lại**,
29
+ không rải trong spec. Phủ đủ các họ: SQL · NoSQL · command · path traversal · XSS lưu trữ.
30
+
31
+ **Mong đợi phải cụ thể:** `400`/`422` + thông điệp lỗi có cấu trúc, hoặc dữ liệu được làm sạch.
32
+ **Không** assert kiểu *"không sập"* — một endpoint trả `200` kèm dữ liệu đã bị nhiễm vẫn "không sập".
33
+
34
+ Thêm một assert: thông điệp lỗi **không rò** stack trace, tên bảng, hay phiên bản thư viện.
35
+
36
+ ## Phase 3 — Trace tag + cột `Script file`
37
+ Như `endpoint.md`, `@trace.test_type=non-functional`.
38
+
39
+ ## Ranh giới
40
+ Đây **không** phải kiểm thâm nhập. Sinh script theo TC đã duyệt; **không** tự mở rộng sang dò
41
+ quét lỗ hổng, và **không** kết luận gì về mức an toàn của hệ thống.
@@ -0,0 +1,35 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-09
4
+ ---
5
+
6
+ # Gen Script — Mobile E2E Journey (Flutter)
7
+
8
+ **Đọc `_shared/mobile-conventions.md` trước.**
9
+
10
+ ## Khi nào trigger
11
+ - Convert TC E2E journey (output `qa-designer/e2e/journey.md`, platform `app`), TC Automatable=Y.
12
+
13
+ ## Khi KHÔNG trigger
14
+ - Test 1 màn/field → `functional/*` · 1 điểm tích hợp → `integration.md`
15
+
16
+ ## Quy ước riêng
17
+ - Chuỗi Page Object xuyên các màn; verify V1…Vn sau mỗi chặng.
18
+ - Journey dài trên thiết bị thật/emulator chậm hơn web đáng kể — timeout cho mỗi bước chờ nên
19
+ rộng hơn mặc định (cold start, animation transition Flutter).
20
+ - Tiền điều kiện (tài khoản, data) qua fixture riêng (`beforeEach`), reset app state
21
+ (`driver.reset()`) trước mỗi journey — không dựa vào journey trước "dọn" giúp.
22
+
23
+ ## Phase 1 — Clarify
24
+ Các màn/PO + hệ thống verify ngoài (nếu có); tài khoản/data cần dựng.
25
+
26
+ ## Phase 2 — Generate
27
+ Mỗi journey → 1 test; verify đủ V1…Vn. Journey phụ thuộc gap → `it.skip('...GAP-xx')`.
28
+
29
+ ## Phase 3 — Self-Verify
30
+ ```bash
31
+ npx tsc --noEmit
32
+ ```
33
+
34
+ ## Output
35
+ `mobile-automation/test/specs/{TICKET-ID}/e2e/<feature>-<scenario>.spec.ts` + Page Object tái dùng.
@@ -0,0 +1,32 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-09
4
+ ---
5
+
6
+ # Gen Script — Mobile Functional Feature (đa màn hình, Flutter)
7
+
8
+ **Đọc `_shared/mobile-conventions.md` trước.**
9
+
10
+ ## Khi nào trigger
11
+ - Convert TC feature đa-màn (output `qa-designer/functional/gui-feature.md`, platform `app`),
12
+ TC Automatable=Y.
13
+
14
+ ## Khi KHÔNG trigger
15
+ - Gọn 1 màn → `functional/screen.md` · đầu-cuối xuyên hệ thống ngoài → `e2e.md`
16
+
17
+ ## Phase 1 — Clarify
18
+ Liệt kê các màn/Page Object cần; state truyền giữa màn (đặc biệt qua navigation Flutter —
19
+ argument truyền route, không phải chỉ URL như web).
20
+
21
+ ## Phase 2 — Generate
22
+ Điều hướng giữa Page Object: test tự import Page Object màn kế tiếp sau action điều hướng
23
+ (singleton pattern WDIO — khác Playwright là PO theo instance `page`). Phủ đúng TC
24
+ Automatable=Y. Assert sau mỗi chặng có side-effect, không dồn về cuối.
25
+
26
+ ## Phase 3 — Self-Verify
27
+ ```bash
28
+ npx tsc --noEmit
29
+ ```
30
+
31
+ ## Output
32
+ Script + nhiều Page Object (mỗi màn) trong `mobile-automation/pageobjects/{TICKET-ID}/...`.
@@ -0,0 +1,42 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-09
4
+ ---
5
+
6
+ # Gen Script — Mobile Functional Screen (1 màn hình, Flutter)
7
+
8
+ **Đọc `_shared/mobile-conventions.md` trước** (Locator Strategy — vì sao cần
9
+ `FlutterIntegration`, Page Object, Wait/Assertion, Common Pitfalls).
10
+
11
+ ## Khi nào trigger
12
+ - Convert TC functional 1 màn (output `qa-designer/functional/gui-screen.md`, platform `app`),
13
+ TC Automatable=Y, `TC_<FEATURE>.md` đã approve.
14
+
15
+ ## Khi KHÔNG trigger
16
+ - Feature đa-màn → `functional/feature.md` · web → dùng bộ `qa-script-designer/web/*`
17
+
18
+ ## Phase 1 — Clarify
19
+ Đọc `TC_<FEATURE>.md` + `TEST_DATA_PLAN.md`. Page Object cho màn này đã có chưa. Đọc contract
20
+ `ValueKey` §4.5.6 tech-doc gộp cho màn này (xem `_shared/mobile-conventions.md` §1.1).
21
+ **§4.5.6 không đủ/không có** → probe widget tree thật bằng Appium Inspector/Flutter DevTools
22
+ trước khi viết locator (xem `_shared/mobile-conventions.md` §1.2) — không đoán `ValueKey` từ
23
+ ảnh chụp màn hình.
24
+
25
+ ## Phase 2 — Generate
26
+ Phủ đúng TC Automatable=Y của màn này (đếm khớp `AUTOMATION_ASSESSMENT.md`). Rào cản kỹ
27
+ thuật (thiếu `ValueKey`, permission dialog chặn) → viết test tốt nhất có thể + đánh dấu
28
+ `it.skip` có lý do + ghi `IMPROVE-xxx`, không bỏ sót TC.
29
+
30
+ Nhóm `describe`: GUI (hiển thị/enable-disable) → Functional (input/validation/action). Data từ
31
+ `TEST_DATA_PLAN.md`.
32
+
33
+ ## Phase 3 — Self-Verify
34
+ ```bash
35
+ npx tsc --noEmit
36
+ ```
37
+ Đối chiếu thủ công số `it(...)` sinh ra = số TC Automatable=Y của màn này.
38
+
39
+ ## Output
40
+ `mobile-automation/test/specs/{TICKET-ID}/<screen-slug>.spec.ts` +
41
+ `mobile-automation/pageobjects/{TICKET-ID}/<screen-slug>.page.ts`
42
+ (xem quy tắc slug/kiểm tra tồn tại trước khi tạo ở `_shared/file-naming-and-folders.md`).
@@ -0,0 +1,39 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-09
4
+ ---
5
+
6
+ # Gen Script — Mobile Integration (App↔Backend)
7
+
8
+ **Đọc `_shared/mobile-conventions.md` trước.** Phần backend (DB/Kafka/API thuần) dùng lại
9
+ đúng client/fixture của `qa-script-designer/web/integration.md` — chỉ khác **phía trigger**
10
+ là thao tác trên app qua WebdriverIO thay vì Playwright.
11
+
12
+ ## Khi nào trigger
13
+ - TC integration app↔backend (output `qa-designer/integration/gui.md` cho platform `app`),
14
+ TC Automatable=Y.
15
+
16
+ ## Quy ước riêng — không mock
17
+ Giống nguyên tắc web: gọi API/DB thật trên môi trường test, không mock response. Khác biệt
18
+ mobile: capture network request từ app khó hơn web (không có `page.waitForResponse()`) —
19
+ dùng 1 trong 2 cách:
20
+ - **Proxy chặn network của thiết bị/emulator** (mitmproxy hoặc tương đương) để verify request
21
+ thật gửi đi — cần setup riêng trong `wdio.conf.ts`.
22
+ - **Verify gián tiếp qua backend**: sau action trên app, query API/DB để xác nhận state đã
23
+ đổi đúng — đơn giản hơn, đủ dùng cho phần lớn TC integration.
24
+
25
+ ## Phase 1 — Clarify
26
+ Chuỗi tích hợp & chặng cần verify; client/fixture backend đã có chưa (tái dùng từ web nếu đã
27
+ tồn tại trong cùng project).
28
+
29
+ ## Phase 2 — Generate
30
+ Mỗi TC → 1 test: action trên app → verify chặng backend (API/DB). Nhóm happy → contract-negative
31
+ → concurrency (nếu áp dụng).
32
+
33
+ ## Phase 3 — Self-Verify
34
+ ```bash
35
+ npx tsc --noEmit
36
+ ```
37
+
38
+ ## Output
39
+ Script trong `mobile-automation/test/specs/{TICKET-ID}/integration/` + client/fixture backend tái dùng.
@@ -0,0 +1,39 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-09
4
+ ---
5
+
6
+ # Gen Script — Mobile Non-Functional (Performance/Security/Compatibility)
7
+
8
+ **Đọc `_shared/mobile-conventions.md` trước.**
9
+
10
+ ## Công cụ theo loại
11
+
12
+ | Loại | Công cụ | Assert |
13
+ |---|---|---|
14
+ | **Performance** | Thời gian cold start (`driver.startActivity`/launch time), thời gian render màn hình đo qua timestamp trước/sau `waitForDisplayed` | Ngưỡng cụ thể (vd cold start < 3s) |
15
+ | **Security** | Test lưu trữ local không mã hoá (kiểm tra file app data qua ADB/`driver.execute('mobile: shell', ...)`), test deep-link injection | Assert dữ liệu nhạy cảm không tồn tại dạng plaintext |
16
+ | **Compatibility** | `capabilities` parametrize theo OS version/device profile trong `wdio.conf.ts`; dùng Device Farm (BrowserStack/Sauce Labs/Firebase Test Lab, xem `_shared/mobile-conventions.md` §5.2) khi cần phủ device thật ngoài máy sẵn có | Mỗi profile = 1 target trong TC |
17
+
18
+ Mobile **không có** hạng mục accessibility qua axe-core (đó là công cụ web); nếu TC yêu cầu
19
+ accessibility mobile, dùng công cụ nền tảng riêng (Android Accessibility Scanner API/iOS
20
+ Accessibility Inspector) — thường vẫn cần một phần thao tác thủ công, đánh dấu rõ trong TC
21
+ gốc thay vì cố automate 100%.
22
+
23
+ ## Quy ước riêng
24
+ - Ngưỡng cụ thể khớp TC gốc; đo elapsed không gồm thời gian setup fixture/app install.
25
+ - Test cần thiết bị/profile đặc biệt → `it.skip(!deviceReady, 'lý do')`.
26
+
27
+ ## Phase 1 — Clarify
28
+ Loại + ngưỡng + công cụ; profile thiết bị cần test.
29
+
30
+ ## Phase 2 — Generate
31
+ Mỗi TC → 1 test đo + assert ngưỡng.
32
+
33
+ ## Phase 3 — Self-Verify
34
+ ```bash
35
+ npx tsc --noEmit
36
+ ```
37
+
38
+ ## Output
39
+ `mobile-automation/test/specs/{TICKET-ID}/non-functional/<feature>-<scenario>.spec.ts` + helper đo (`utils/measure.ts`).
@@ -0,0 +1,36 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-09
4
+ ---
5
+
6
+ # Gen Script — Web E2E Journey
7
+
8
+ **Đọc `_shared/web-conventions.md` trước.**
9
+
10
+ ## Khi nào trigger
11
+ - Convert TC E2E journey (output `qa-designer/e2e/journey.md`), TC Automatable=Y.
12
+
13
+ ## Khi KHÔNG trigger
14
+ - Test 1 màn/field → `functional/*` · 1 điểm tích hợp → `integration.md`
15
+
16
+ ## Quy ước riêng
17
+ - Chuỗi Page Object xuyên các màn; verify points (V1…Vn của journey) thành `expect()` rõ ràng
18
+ **sau mỗi chặng**, không dồn hết về cuối (Common Pitfall #4).
19
+ - Tiền điều kiện phức tạp (tài khoản role, dữ liệu buổi/lớp) qua fixture Playwright riêng,
20
+ không inline trong test; cleanup sau journey.
21
+ - Cần verify hệ thống ngoài (DB/CRM) → client/fixture riêng, giống `integration.md`.
22
+
23
+ ## Phase 1 — Clarify
24
+ Các màn/PO + hệ thống verify; tài khoản/data cần dựng; điểm cleanup.
25
+
26
+ ## Phase 2 — Generate
27
+ Mỗi journey → 1 test; verify đủ V1…Vn. Journey phụ thuộc gap → `test.fixme('...GAP-xx')`.
28
+
29
+ ## Phase 3 — Self-Verify
30
+ ```bash
31
+ npx tsc --noEmit
32
+ npx playwright test automation/tests/{TICKET-ID}/e2e/<feature>-<scenario>.spec.ts --list
33
+ ```
34
+
35
+ ## Output
36
+ `automation/tests/{TICKET-ID}/e2e/<feature>-<scenario>.spec.ts` + Page Object/client tái dùng.