synapse-hub 2.4.3 → 2.6.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.
Files changed (184) hide show
  1. package/.agents/agents/failure-analyst.md +13 -0
  2. package/.agents/agents/orchestrator.md +5 -2
  3. package/.agents/agents/pom-crafter.md +13 -0
  4. package/.agents/agents/quality-gatekeeper.md +21 -2
  5. package/.agents/agents/test-runner.md +11 -0
  6. package/.agents/rules/context-engineering.md +22 -102
  7. package/.agents/rules/mobile_appium_rules.md +19 -115
  8. package/.agents/rules/performance_testing_rules.md +18 -62
  9. package/.agents/rules/skill-precedence.md +24 -24
  10. package/.agents/rules/systematic-debugging.md +10 -27
  11. package/.agents/skills/lean-code-ladder/SKILL.md +12 -1
  12. package/.agents/skills/lean-impact-scoreboard/SKILL.md +48 -0
  13. package/.agents/skills/lean-impact-scoreboard/examples/sample_benchmark_gain.md +20 -0
  14. package/.agents/skills/lean-impact-scoreboard/examples/sample_scoreboard.md +15 -0
  15. package/.agents/skills/repo-quality-audit/SKILL.md +68 -0
  16. package/.agents/skills/repo-quality-audit/examples/sample_audit_walkthrough.md +15 -0
  17. package/.agents/skills/repo-quality-audit/examples/sample_repo_audit.md +33 -0
  18. package/.agents/skills/senior-code-review/SKILL.md +90 -0
  19. package/.agents/skills/senior-code-review/examples/sample_review_walkthrough.md +29 -0
  20. package/.agents/skills/technical-debt-harvester/SKILL.md +70 -0
  21. package/.agents/skills/technical-debt-harvester/examples/sample_debt_ledger.md +25 -0
  22. package/.agents/skills/technical-debt-harvester/examples/sample_ledger_output.md +20 -0
  23. package/.agents/skills/token-and-context-optimizer/references/context_engineering_guide.md +49 -0
  24. package/.cursor/rules/synapse-qa.mdc +4 -3
  25. package/.cursorrules +1 -0
  26. package/.windsurfrules +1 -0
  27. package/AGENTS.md +57 -180
  28. package/CLAUDE.md +5 -0
  29. package/NOTICE +40 -4
  30. package/README.en.md +33 -13
  31. package/README.md +36 -16
  32. package/bin/agent-qa.js +3 -2
  33. package/docs/ARCHITECTURE.md +39 -11
  34. package/docs/CLI_REFERENCE.md +15 -1
  35. package/docs/adopted-ecosystem-tools.md +9 -0
  36. package/docs/research/agent_skill_training_analysis.md +1357 -0
  37. package/package.json +1 -3
  38. package/skills_manifest.json +39 -3
  39. package/synapse.config.json +3 -2
  40. package/templates/ai_bridges/CLAUDE.md +5 -0
  41. package/templates/ai_bridges/copilot-instructions.md +2 -0
  42. package/templates/ai_bridges/cursor_synapse_qa.mdc +4 -3
  43. package/templates/ai_bridges/windsurfrules +1 -0
  44. package/templates/welcome.html.tmpl +34 -25
  45. package/tools/.infer_baseline.json +7 -1079
  46. package/tools/TOOLS_RUNBOOK.md +182 -13
  47. package/tools/__init__.py +1 -1
  48. package/tools/agent_canary.py +4 -4
  49. package/tools/analyze_project.py +2 -2
  50. package/tools/api_fuzzer.py +4 -4
  51. package/tools/auto_heal.py +14 -2
  52. package/tools/auto_heal_reflexion.py +172 -0
  53. package/tools/browser_helpers.js +57 -2
  54. package/tools/bump_version.py +30 -2
  55. package/tools/cache_resolver.py +6 -6
  56. package/tools/check_env.py +13 -7
  57. package/tools/cli.py +265 -197
  58. package/tools/count_tokens.py +7 -7
  59. package/tools/dashboard_generator.py +39 -23
  60. package/tools/db_sandbox.py +5 -5
  61. package/tools/debt_harvester.py +236 -0
  62. package/tools/distill_log.py +2 -2
  63. package/tools/dspy_qa_signatures.py +357 -0
  64. package/tools/flaky_detector.py +5 -5
  65. package/tools/generate_tc_from_story.py +2 -2
  66. package/tools/generate_test_reports.py +4 -4
  67. package/tools/git_pre_commit_guard.py +6 -6
  68. package/tools/infer_linter.py +12 -7
  69. package/tools/interview_story.py +117 -0
  70. package/tools/lint_ai_docs.py +3 -3
  71. package/tools/matrix_sharder.py +4 -4
  72. package/tools/mcp_excel_server.py +1 -1
  73. package/tools/mcp_server.py +3 -3
  74. package/tools/micro_mutator.py +6 -6
  75. package/tools/omni_transpiler.py +2 -2
  76. package/tools/output_formatter.py +37 -2
  77. package/tools/pinpoint_failure.py +4 -1
  78. package/tools/repo_map.py +2 -2
  79. package/tools/reversible_sandbox.py +157 -0
  80. package/tools/safety_firewall.py +30 -2
  81. package/tools/senior_code_review.py +341 -0
  82. package/tools/setup_project.py +9 -9
  83. package/tools/skill_collision_guard.py +231 -0
  84. package/tools/skill_vault.py +343 -0
  85. package/tools/skillspector.py +2 -2
  86. package/tools/synapse_qa_bench.py +190 -0
  87. package/tools/sync_agent_rules.py +14 -3
  88. package/tools/test_impact.py +4 -4
  89. package/tools/textgrad_mutation_optimizer.py +234 -0
  90. package/tools/tms_sync_bridge.py +2 -2
  91. package/tools/update_sentinel.py +17 -17
  92. package/tools/verify_all.py +47 -16
  93. package/tools/visual_som_helper.py +8 -8
  94. package/tools/watch_mode.py +7 -8
  95. package/tools/wcag_audit.py +5 -1
  96. package/tools/welcome_page.py +12 -12
  97. package/benchmarks/qa_eval/README.md +0 -71
  98. package/benchmarks/qa_eval/build_dataset.py +0 -677
  99. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-06_api_contract_drift.json +0 -22
  100. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-07_api_contract_drift.json +0 -22
  101. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-08_api_contract_drift.json +0 -22
  102. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-09_api_contract_drift.json +0 -22
  103. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-10_api_contract_drift.json +0 -22
  104. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-21_fake_pass_tautology.json +0 -21
  105. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-22_fake_pass_tautology.json +0 -21
  106. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-23_fake_pass_tautology.json +0 -21
  107. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-24_fake_pass_tautology.json +0 -21
  108. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-25_fake_pass_tautology.json +0 -21
  109. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-01_locator_drift.json +0 -23
  110. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-02_locator_drift.json +0 -23
  111. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-03_locator_drift.json +0 -23
  112. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-04_locator_drift.json +0 -23
  113. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-05_locator_drift.json +0 -23
  114. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-16_stale_and_mutation.json +0 -22
  115. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-17_stale_and_mutation.json +0 -22
  116. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-18_stale_and_mutation.json +0 -22
  117. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-19_stale_and_mutation.json +0 -22
  118. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-20_stale_and_mutation.json +0 -23
  119. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-11_timing_and_race.json +0 -22
  120. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-12_timing_and_race.json +0 -22
  121. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-13_timing_and_race.json +0 -22
  122. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-14_timing_and_race.json +0 -22
  123. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-15_timing_and_race.json +0 -22
  124. package/benchmarks/qa_eval/eval_runner.py +0 -418
  125. package/examples/cypress-ecommerce-suite/README.md +0 -45
  126. package/examples/cypress-ecommerce-suite/cypress/e2e/checkout.cy.js +0 -17
  127. package/examples/cypress-ecommerce-suite/cypress/pages/BasePage.js +0 -29
  128. package/examples/cypress-ecommerce-suite/cypress/pages/CartPage.js +0 -51
  129. package/examples/cypress-ecommerce-suite/cypress.config.js +0 -13
  130. package/examples/cypress-ecommerce-suite/package.json +0 -13
  131. package/examples/katalon-suite/Keywords/pages/BasePage.groovy +0 -19
  132. package/examples/katalon-suite/Keywords/pages/DashboardPage.groovy +0 -29
  133. package/examples/katalon-suite/Keywords/pages/LoginPage.groovy +0 -45
  134. package/examples/katalon-suite/Profiles/default.glbl +0 -12
  135. package/examples/katalon-suite/README.md +0 -18
  136. package/examples/katalon-suite/Scripts/Auth/TC_LoginSuccess/Script1234567890.groovy +0 -12
  137. package/examples/katalon-suite/Scripts/Dashboard/TC_ViewSummary/Script1234567891.groovy +0 -15
  138. package/examples/katalon-suite/katalon_suite.prj +0 -21
  139. package/examples/playwright-crm-suite/README.md +0 -40
  140. package/examples/playwright-crm-suite/package.json +0 -14
  141. package/examples/playwright-crm-suite/pages/BasePage.ts +0 -37
  142. package/examples/playwright-crm-suite/pages/CustomerPage.ts +0 -57
  143. package/examples/playwright-crm-suite/pages/LoginPage.ts +0 -32
  144. package/examples/playwright-crm-suite/playwright.config.ts +0 -27
  145. package/examples/playwright-crm-suite/tests/auth.spec.ts +0 -22
  146. package/examples/playwright-crm-suite/tests/customers.spec.ts +0 -33
  147. package/examples/plugins/sample-custom-plugin/README.md +0 -14
  148. package/examples/plugins/sample-custom-plugin/plugin.json +0 -16
  149. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/SKILL.md +0 -29
  150. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/examples/audit_fixture.json +0 -8
  151. package/examples/plugins/sample-custom-plugin/tools/custom_security_headers.py +0 -125
  152. package/examples/practices/README.md +0 -22
  153. package/examples/practices/requirements/crm/requirements_contracts.md +0 -292
  154. package/examples/practices/requirements/crm/requirements_customers.md +0 -462
  155. package/examples/practices/requirements/crm/requirements_login.md +0 -202
  156. package/examples/practices/requirements/crm/requirements_projects.md +0 -396
  157. package/examples/practices/requirements/crm/requirements_tasks.md +0 -343
  158. package/examples/practices/requirements/ecommerce/requirements_category_brand.txt +0 -402
  159. package/examples/practices/requirements/jira/KAN-5/KAN-1/2026-04-03_04-28-27.png +0 -0
  160. package/examples/practices/requirements/jira/KAN-5/KAN-1/KAN-1_requirement.md +0 -52
  161. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-19.png +0 -0
  162. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-57.png +0 -0
  163. package/examples/practices/requirements/jira/KAN-5/KAN-4/KAN-4_requirement.md +0 -119
  164. package/examples/practices/requirements/jira/KAN-5/KAN-5_overview.md +0 -9
  165. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-08.png +0 -0
  166. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-23.png +0 -0
  167. package/examples/practices/requirements/jira/KAN-5/KAN-6/KAN-6_requirement.md +0 -32
  168. package/examples/practices/testcases/api/book_management_api_test_cases.md +0 -144
  169. package/examples/practices/testcases/api/user_management_api_test_cases.md +0 -139
  170. package/examples/practices/testcases/crm/test_cases_crm_login.md +0 -148
  171. package/examples/practices/testcases/crm/test_cases_customers.md +0 -156
  172. package/examples/pytest-api-suite/README.md +0 -45
  173. package/examples/pytest-api-suite/conftest.py +0 -31
  174. package/examples/pytest-api-suite/pages/user_api_client.py +0 -36
  175. package/examples/pytest-api-suite/requirements.txt +0 -2
  176. package/examples/pytest-api-suite/tests/test_user_api.py +0 -44
  177. package/examples/selenium-suite/README.md +0 -18
  178. package/examples/selenium-suite/pages/base_page.py +0 -27
  179. package/examples/selenium-suite/pages/login_page.py +0 -29
  180. package/examples/selenium-suite/pages/order_page.py +0 -25
  181. package/examples/selenium-suite/pytest.ini +0 -6
  182. package/examples/selenium-suite/requirements.txt +0 -2
  183. package/examples/selenium-suite/tests/test_login.py +0 -10
  184. package/examples/selenium-suite/tests/test_order.py +0 -10
@@ -1,156 +0,0 @@
1
- # TEST CASES — CRM CUSTOMERS MODULE
2
-
3
- | Thông tin | Chi tiết |
4
- |-----------|----------|
5
- | **Dự án** | CRM (Perfex CRM - Anh Tester Demo) |
6
- | **Module** | Customers (Clients) |
7
- | **URL** | https://crm.anhtester.com/admin/clients |
8
- | **Phương pháp** | AI-RBT (Risk-Based Testing) — FULL 6 bước |
9
- | **Ngày tạo** | 2026-05-21 |
10
- | **Tổng số TC** | 48 |
11
-
12
- ---
13
-
14
- ## TỔNG HỢP RISK LEVEL
15
-
16
- | Module / Sub-module | Risk Level | Số TC |
17
- |---------------------|-----------|-------|
18
- | SUB-02: Create & Edit Customer | 🔴 High | 13 |
19
- | SUB-04: Contacts Management | 🔴 High | 10 |
20
- | SUB-05: Data Integration (Import/Export) | 🔴 High | 8 |
21
- | SUB-06: Bulk Actions & Advanced Filters | 🟡 Medium | 7 |
22
- | SUB-01: Summary & List View | 🟡 Medium | 6 |
23
- | SUB-03: Customer Detail Profile & Sidebar | 🟢 Low | 4 |
24
-
25
- ---
26
-
27
- ## TRACEABILITY MATRIX
28
-
29
- | Mã Yêu Cầu (Requirement ID) | Test Cases (TC ID) |
30
- |:----------------------------|:-------------------|
31
- | **FR-CUST-001** (Xem danh sách) | CRM_CUST_TC_002, CRM_CUST_TC_003, CRM_CUST_TC_004, CRM_CUST_TC_005, CRM_CUST_TC_006, CRM_CUST_TC_007 |
32
- | **FR-CUST-002** (Tạo mới) | CRM_CUST_TC_008, CRM_CUST_TC_009, CRM_CUST_TC_010, CRM_CUST_TC_011, CRM_CUST_TC_012, CRM_CUST_TC_013, CRM_CUST_TC_014, CRM_CUST_TC_015, CRM_CUST_TC_016, CRM_CUST_TC_017, CRM_CUST_TC_018, CRM_CUST_TC_019 |
33
- | **FR-CUST-003** (Chi tiết & Chỉnh sửa) | CRM_CUST_TC_020, CRM_CUST_TC_021, CRM_CUST_TC_022, CRM_CUST_TC_023 |
34
- | **FR-CUST-004** (Xóa khách hàng) | CRM_CUST_TC_024, CRM_CUST_TC_025 |
35
- | **FR-CUST-005** (Quản lý Contacts) | CRM_CUST_TC_026, CRM_CUST_TC_027, CRM_CUST_TC_028, CRM_CUST_TC_029, CRM_CUST_TC_030, CRM_CUST_TC_031, CRM_CUST_TC_032, CRM_CUST_TC_033, CRM_CUST_TC_034 |
36
- | **FR-CUST-006** (Import khách hàng) | CRM_CUST_TC_035, CRM_CUST_TC_036, CRM_CUST_TC_037, CRM_CUST_TC_038 |
37
- | **FR-CUST-007** (Export dữ liệu) | CRM_CUST_TC_039, CRM_CUST_TC_040, CRM_CUST_TC_041, CRM_CUST_TC_042 |
38
- | **FR-CUST-008** (Bulk Actions) | CRM_CUST_TC_043, CRM_CUST_TC_044, CRM_CUST_TC_045 |
39
- | **FR-CUST-009** (Tìm kiếm & Lọc) | CRM_CUST_TC_046, CRM_CUST_TC_047, CRM_CUST_TC_048 |
40
- | **Summary Statistics** | CRM_CUST_TC_001 |
41
-
42
- ---
43
-
44
- ## BẢNG TEST CASES
45
-
46
- ### SUB-01: Summary & List View (🟡 Medium Risk)
47
-
48
- | TC ID | Module | Risk Level | Test Title | Pre-Condition | Test Steps | Expected Result | Priority | Test Data |
49
- |-------|--------|------------|------------|---------------|------------|-----------------|----------|-----------|
50
- | CRM_CUST_TC_001 | SUB-01: List View | 🟡 Medium | Verify tóm tắt thống kê Customers Summary hiển thị đúng chỉ số | Admin đã đăng nhập và đang ở trang danh sách khách hàng | 1. Quan sát phần đầu trang tại `/admin/clients`<br>2. So sánh tổng số khách hàng thực tế trong DataTable với số lượng hiển thị ở bảng tóm tắt | 1. Hiển thị đầy đủ 6 chỉ số:<br>- Total Customers<br>- Active Customers<br>- Inactive Customers<br>- Active Contacts<br>- Inactive Contacts<br>- Contacts Logged In Today<br>2. Số lượng ở bảng thống kê khớp chính xác với dữ liệu thực tế | Medium | N/A |
51
- | CRM_CUST_TC_002 | SUB-01: List View | 🟡 Medium | Verify DataTable hiển thị đầy đủ các cột thông tin khách hàng | Admin đã đăng nhập | 1. Truy cập `/admin/clients`<br>2. Quan sát tiêu đề các cột của DataTable danh sách khách hàng | 1. DataTable tải thành công<br>2. Hiển thị đúng các cột:<br>- Checkbox ở cột đầu tiên<br>- `#` (ID)<br>- Company<br>- Primary Contact<br>- Primary Email<br>- Phone<br>- Active (Toggle switch)<br>- Groups<br>- Date Created | High | N/A |
52
- | CRM_CUST_TC_003 | SUB-01: List View | 🟡 Medium | Verify chức năng phân trang (Pagination) trên DataTable | Hệ thống có trên 25 khách hàng | 1. Truy cập danh sách khách hàng<br>2. Chọn số lượng hiển thị là 25 khách hàng từ dropdown<br>3. Click nút chuyển trang tiếp theo (`Next`) ở cuối DataTable<br>4. Chọn số lượng hiển thị là 50 khách hàng | 1. Danh sách hiển thị trang 1<br>2. Bảng hiển thị tối đa 25 dòng<br>3. Chuyển trang thành công, hiển thị các khách hàng tiếp theo, thông tin phân trang cập nhật đúng<br>4. Bảng thay đổi số lượng hiển thị thành tối đa 50 dòng | Medium | Phân trang: 25, 50 dòng |
53
- | CRM_CUST_TC_004 | SUB-01: List View | 🟡 Medium | Verify chức năng sắp xếp (Sort) tăng/giảm dần theo cột Company | Hệ thống có nhiều khách hàng với tên công ty khác nhau | 1. Truy cập danh sách khách hàng<br>2. Click vào tiêu đề cột `Company`<br>3. Click vào tiêu đề cột `Company` lần nữa | 1. Trang danh sách hiển thị<br>2. Cột Company được sắp xếp theo thứ tự chữ cái A-Z<br>3. Cột Company đảo ngược sắp xếp theo thứ tự Z-A | Medium | N/A |
54
- | CRM_CUST_TC_005 | SUB-01: List View | 🟡 Medium | Verify bật/tắt nhanh trạng thái Active/Inactive của khách hàng bằng toggle switch | Có ít nhất 1 khách hàng đang hoạt động trong danh sách | 1. Truy cập danh sách khách hàng<br>2. Tìm khách hàng đang có toggle switch là ON (Active)<br>3. Click vào toggle switch của khách hàng này để chuyển sang OFF (Inactive)<br>4. Tải lại trang (F5) và kiểm tra lại trạng thái khách hàng đó | 1. Trang danh sách hiển thị<br>2-3. Toggle chuyển từ màu xanh (ON) sang màu xám (OFF). Không có reload trang, thông báo cập nhật thành công hiển thị nhanh góc màn hình<br>4. Trạng thái khách hàng vẫn được lưu là Inactive sau khi F5 | High | Khách hàng: `Anh Tester Company` |
55
- | CRM_CUST_TC_006 | SUB-01: List View | 🟡 Medium | Verify các liên kết điều hướng trên DataTable hoạt động đúng | Có sẵn khách hàng có đầy đủ tên Company và số điện thoại | 1. Truy cập danh sách khách hàng<br>2. Click vào link tên công ty ở cột `Company`<br>3. Quay lại trang danh sách, click vào link số điện thoại ở cột `Phone` | 1. Trang danh sách hiển thị<br>2. Chuyển hướng thành công sang trang chi tiết khách hàng `/admin/clients/client/{id}`<br>3. Trình duyệt kích hoạt ứng dụng gọi điện mặc định trên thiết bị (hoặc hiển thị tel link) | Medium | N/A |
56
-
57
- ### SUB-02: Create & Edit Customer (🔴 High Risk)
58
-
59
- | TC ID | Module | Risk Level | Test Title | Pre-Condition | Test Steps | Expected Result | Priority | Test Data |
60
- |-------|--------|------------|------------|---------------|------------|-----------------|----------|-----------|
61
- | CRM_CUST_TC_007 | SUB-02: Create/Edit | 🔴 High | Mở form tạo mới Customer thành công | Đang ở trang danh sách khách hàng | 1. Click nút "+ New Customer" ở trên đầu trang | 1. Chuyển hướng thành công sang `/admin/clients/client`<br>2. Form "Add new customer" hiển thị đầy đủ các trường nhập liệu<br>3. Tab "Customer Details" được chọn làm mặc định | High | N/A |
62
- | CRM_CUST_TC_008 | SUB-02: Create/Edit | 🔴 High | Tạo mới khách hàng thành công với thông tin tối thiểu (Happy Path) | Form tạo mới đang mở | 1. Nhập trường bắt buộc: Company = `Company_Auto_17120492` (chuỗi ngẫu nhiên)<br>2. Để trống tất cả các trường khác<br>3. Click nút "Save" ở góc dưới bên phải | 1. Lưu thông tin thành công<br>2. Hệ thống chuyển hướng về trang chi tiết khách hàng mới tạo (`/admin/clients/client/{id}`)<br>3. Hiển thị thông báo lưu thành công màu xanh lá | Critical | Company: `Company_Auto_17120492` |
63
- | CRM_CUST_TC_009 | SUB-02: Create/Edit | 🔴 High | Tạo mới khách hàng thành công với đầy đủ thông tin trên cả 2 tab (Happy Path) | Form tạo mới đang mở, các Groups, Currency, Country đã được cấu hình trong hệ thống | 1. Nhập thông tin tab **Customer Details**:<br>- Company: `Perfex_Auto_VIP_1712`<br>- VAT: `VAT987654321`<br>- Phone: `0987654321`<br>- Website: `https://anhtester.com`<br>- Groups: Chọn "VIP" và "Wholesale"<br>- Currency: Chọn "USD"<br>- Default Language: Chọn "Vietnamese"<br>- Address: `123 Đường CMT8, Quận 1`<br>- City: `Hồ Chí Minh`<br>- State: `Sài Gòn`<br>- Zip Code: `700000`<br>- Country: Chọn "Vietnam"<br>2. Chuyển sang tab **Billing & Shipping**:<br>- Click link `"Same as Customer Info"` ở phần Billing Address<br>- Click link `"Copy Billing Address"` ở phần Shipping Address<br>3. Click nút "Save" | 1. Dữ liệu tab Customer Details được điền đầy đủ<br>2. Dữ liệu địa chỉ ở tab 2 tự động điền giống thông tin đã nhập ở tab 1<br>3. Lưu thành công, chuyển hướng đến trang chi tiết khách hàng mới và hiển thị đầy đủ các thông tin đã nhập chính xác | Critical | Toàn bộ dữ liệu nhập bên cột Test Data |
64
- | CRM_CUST_TC_010 | SUB-02: Create/Edit | 🔴 High | Validate trường Company bắt buộc nhập (Required validation) | Form tạo mới đang mở | 1. Để trống trường Company<br>2. Click nút "Save" | 1. Form không được submit<br>2. Dưới trường Company hiển thị thông báo lỗi màu đỏ: `"This field is required."`<br>3. Trình duyệt không chuyển trang | Critical | Company: (trống) |
65
- | CRM_CUST_TC_011 | SUB-02: Create/Edit | 🔴 High | Field-Level Validation: Validate độ dài tối đa (maxlength) trường Company | Form tạo mới đang mở | 1. Nhập chuỗi 256 ký tự vào trường `Company` (vượt quá giới hạn max 255 ký tự)<br>2. Click nút "Save" | 1. Hệ thống xử lý an toàn: Có thể tự động cắt chuỗi còn 255 ký tự HOẶC chặn submit hiển thị thông báo lỗi phù hợp<br>2. Không xảy ra lỗi crash hệ thống (lỗi 500) | High | Company: Chuỗi 256 ký tự 'a' |
66
- | CRM_CUST_TC_012 | SUB-02: Create/Edit | 🔴 High | Field-Level Validation: Chống XSS Script Injection vào trường Company | Form tạo mới đang mở | 1. Nhập mã XSS vào trường Company: `<script>alert('XSS_CUST')</script>`<br>2. Click nút "Save"<br>3. Kiểm tra tên công ty hiển thị trên giao diện chi tiết và danh sách DataTable | 1-2. Hệ thống lưu thành công hoặc báo lỗi an toàn, không crash<br>3. Tên công ty hiển thị dưới dạng chuỗi văn bản thông thường (HTML escaped), **KHÔNG** kích hoạt script chạy popup cảnh báo | Critical | Company: `<script>alert('XSS_CUST')</script>` |
67
- | CRM_CUST_TC_013 | SUB-02: Create/Edit | 🔴 High | Field-Level Validation: Chống SQL Injection vào trường Company | Form tạo mới đang mở | 1. Nhập mã SQL Injection vào trường Company: `ClientTest' OR 1=1 --`<br>2. Click nút "Save"<br>3. Kiểm tra hệ thống lưu dữ liệu | 1-2. Hệ thống lưu thành công hoặc báo lỗi an toàn, không crash<br>3. Bản ghi được tạo mới với tên công ty đúng như chuỗi nhập vào, cấu trúc database an toàn không bị phá vỡ | Critical | Company: `ClientTest' OR 1=1 --` |
68
- | CRM_CUST_TC_014 | SUB-02: Create/Edit | 🔴 High | Field-Level Validation: Validate định dạng nhập liệu trường Phone | Form tạo mới đang mở | 1. Nhập chữ cái và ký tự lạ vào trường Phone: `Phone1234abcd!@`<br>2. Click "Save" | 1. Dữ liệu được chấp nhận hoặc hệ thống tự động lọc bỏ ký tự không hợp lệ tùy quy tắc backend<br>2. Không xảy ra lỗi crash hệ thống | High | Phone: `Phone1234abcd!@` |
69
- | CRM_CUST_TC_015 | SUB-02: Create/Edit | 🔴 High | Field-Level Validation: Validate định dạng nhập liệu trường Website | Form tạo mới đang mở | 1. Nhập chuỗi không phải định dạng URL vào trường Website: `invalid-website-format`<br>2. Click "Save" | 1. Hệ thống xử lý an toàn (chấp nhận lưu dạng text hoặc báo lỗi định dạng URL không hợp lệ)<br>2. Không xảy ra lỗi crash hệ thống | High | Website: `invalid-website-format` |
70
- | CRM_CUST_TC_016 | SUB-02: Create/Edit | 🔴 High | Field-Level Validation: Chọn nhiều giá trị cho dropdown Groups | Form tạo mới đang mở, có sẵn các Groups: "VIP", "Wholesale", "Retail" | 1. Click vào trường dropdown Groups<br>2. Chọn lần lượt "VIP" và "Wholesale"<br>3. Click ra ngoài dropdown và kiểm tra hiển thị lựa chọn | 1. Dropdown mở ra danh sách group<br>2. Cả 2 nhóm "VIP" và "Wholesale" được tích chọn<br>3. Trường Groups hiển thị 2 nhãn (tags) đại diện cho 2 group đã chọn | Medium | Groups: VIP, Wholesale |
71
- | CRM_CUST_TC_017 | SUB-02: Create/Edit | 🔴 High | Tạo mới khách hàng bằng nút "Save and create contact" | Form tạo mới đang mở | 1. Nhập Company: `Company_Contact_Link_01`<br>2. Click nút "Save and create contact" | 1. Hệ thống lưu khách hàng thành công<br>2. Chuyển hướng trực tiếp sang modal/form tạo mới Contact của khách hàng đó (`Add contact`) | High | Company: `Company_Contact_Link_01` |
72
- | CRM_CUST_TC_018 | SUB-02: Create/Edit | 🔴 High | Chỉnh sửa thông tin khách hàng hiện tại thành công | Đã có khách hàng hiện hữu trong hệ thống | 1. Vào trang chi tiết khách hàng `/admin/clients/client/{id}`<br>2. Tại tab Customer Details, sửa trường Company từ `Tên Cũ` thành `Tên Mới Được Cập Nhật`<br>3. Click nút "Save" ở góc dưới bên phải | 1. Vào trang chi tiết thành công<br>2. Tên mới được nhập vào trường<br>3. Lưu thay đổi thành công, hệ thống hiển thị thông báo cập nhật thành công, tên mới được áp dụng và giữ lại sau khi reload trang | High | Company: `Tên Mới Được Cập Nhật` |
73
- | CRM_CUST_TC_019 | SUB-02: Create/Edit | 🔴 High | Tự động điền thông tin địa chỉ với nút "Same as Customer Info" và "Copy Billing Address" | Form tạo mới đang mở | 1. Tại tab Customer Details, nhập Address = `456 CMT8`, City = `Cần Thơ`, Country = `Vietnam`<br>2. Chuyển qua tab Billing & Shipping<br>3. Click nút `"Same as Customer Info"` ở cột Billing Address<br>4. Click nút `"Copy Billing Address"` ở cột Shipping Address | 1-2. Thông tin cơ bản được điền<br>3. Thông tin địa chỉ Billing Address tự động điền giống thông tin Customer Details (Street: 456 CMT8, City: Cần Thơ, Country: Vietnam)<br>4. Thông tin Shipping Address tự động điền giống Billing Address | Medium | N/A |
74
-
75
- ### SUB-03: Customer Detail Profile & Sidebar Navigation (🟢 Low Risk)
76
-
77
- | TC ID | Module | Risk Level | Test Title | Pre-Condition | Test Steps | Expected Result | Priority | Test Data |
78
- |-------|--------|------------|------------|---------------|------------|-----------------|----------|-----------|
79
- | CRM_CUST_TC_020 | SUB-03: Detail View | 🟢 Low | Verify hiển thị tiêu đề Header và tab Profile mặc định | Đang ở trang chi tiết một khách hàng cụ thể | 1. Truy cập `/admin/clients/client/{customer_id}`<br>2. Quan sát Header và nội dung chính bên phải | 1. Header hiển thị đúng format: `#{ID} {Tên Công Ty}`<br>2. Label "Profile" hiển thị ở bên phải header<br>3. Tab Profile được chọn mặc định và hiển thị form chỉnh sửa | Low | ID: `1`, Company: `Anh Tester Company` |
80
- | CRM_CUST_TC_021 | SUB-03: Detail View | 🟢 Low | Verify điều hướng mượt mà qua các Tab ở sidebar trái | Đang ở trang chi tiết khách hàng | 1. Click lần lượt vào các tab ở sidebar trái: `Contacts`, `Notes`, `Statement`, `Invoices`, `Projects`, `Tasks`, `Tickets`<br>2. Quan sát nội dung tải tương ứng bên phải | 1. Chuyển tab nhanh chóng mà không reload lại toàn bộ trang<br>2. Mỗi tab hiển thị đúng nội dung và bảng thông tin tương ứng với tab đó | Medium | N/A |
81
- | CRM_CUST_TC_022 | SUB-03: Detail View | 🟢 Low | Verify tính năng gán quản lý trong tab Customer Admins | Đang ở trang chi tiết khách hàng, có sẵn nhân viên Staff trong hệ thống | 1. Tại tab Profile, click sub-tab `Customer Admins`<br>2. Click dropdown Chọn Admins và chọn 1 nhân viên: `Staff_Tester_01`<br>3. Click "Save" | 1. Sub-tab Customer Admins hiển thị đúng<br>2. Nhân viên được chọn hiển thị trong dropdown<br>3. Lưu thành công, nhân viên được gán thành công làm quản lý cho khách hàng này | Medium | Admin gán: `Staff_Tester_01` |
82
- | CRM_CUST_TC_023 | SUB-03: Detail View | 🟢 Low | Verify hủy bỏ thao tác xóa khách hàng (Cancel Delete) | Đang ở trang chi tiết khách hàng | 1. Click vào biểu tượng dropdown action bên cạnh tên công ty trên header<br>2. Click nút "Delete"<br>3. Khi popup xác nhận hiện ra, click nút "Cancel" | 1-2. Popup xác nhận hiển thị với câu hỏi có chắc chắn muốn xóa không<br>3. Popup đóng lại, khách hàng KHÔNG bị xóa, hệ thống vẫn ở trang chi tiết | Medium | N/A |
83
-
84
- ### SUB-04: Contacts Management (🔴 High Risk)
85
-
86
- | TC ID | Module | Risk Level | Test Title | Pre-Condition | Test Steps | Expected Result | Priority | Test Data |
87
- |-------|--------|------------|------------|---------------|------------|-----------------|----------|-----------|
88
- | CRM_CUST_TC_024 | SUB-04: Contacts | 🔴 High | Verify bảng Contacts trống khi khách hàng mới tạo chưa có contact | Khách hàng mới được tạo thành công và chưa được gán contact nào | 1. Vào trang chi tiết khách hàng<br>2. Click tab `Contacts` ở sidebar trái | 1. Tab Contacts tải thành công<br>2. Bảng Contacts trống và hiển thị thông báo: `"No entries found"` | Low | N/A |
89
- | CRM_CUST_TC_025 | SUB-04: Contacts | 🔴 High | Thêm mới Contact cho khách hàng thành công (Happy Path) | Đang ở tab Contacts của một khách hàng cụ thể | 1. Click nút "New Contact"<br>2. Điền đầy đủ thông tin trong form Popup:<br>- First Name: `An`<br>- Last Name: `Tester`<br>- Email: `an_tester_1712@auto.test` (unique)<br>- Password: `SecurePass123`<br>3. Click nút "Save" | 1. Popup "Add new contact" hiển thị<br>2. Thông tin được điền đầy đủ<br>3. Lưu thành công, popup đóng lại, hiển thị thông báo thành công và contact `An Tester` xuất hiện trên bảng Contacts của khách hàng | Critical | First Name: `An`<br>Last Name: `Tester`<br>Email: `an_tester_1712@auto.test`<br>Password: `SecurePass123` |
90
- | CRM_CUST_TC_026 | SUB-04: Contacts | 🔴 High | Field-Level Validation: Validate trường First Name của Contact để trống | Đang mở popup Add new contact | 1. Để trống trường First Name<br>2. Điền các trường khác hợp lệ<br>3. Click nút "Save" | 1. Popup báo lỗi dưới trường First Name: `"This field is required."`<br>2. Form không được lưu, popup không đóng | High | First Name: (trống)<br>Last Name: `Tester`<br>Email: `test_contact_01@auto.test` |
91
- | CRM_CUST_TC_027 | SUB-04: Contacts | 🔴 High | Field-Level Validation: Validate trường Last Name của Contact để trống | Đang mở popup Add new contact | 1. Điền First Name: `An`<br>2. Để trống trường Last Name<br>3. Click "Save" | 1. Popup báo lỗi dưới trường Last Name: `"This field is required."`<br>2. Form không được lưu | High | First Name: `An`<br>Last Name: (trống)<br>Email: `test_contact_02@auto.test` |
92
- | CRM_CUST_TC_028 | SUB-04: Contacts | 🔴 High | Field-Level Validation: Validate trường Email của Contact để trống | Đang mở popup Add new contact | 1. Nhập First/Last name<br>2. Để trống trường Email<br>3. Click "Save" | 1. Popup báo lỗi dưới trường Email: `"This field is required."`<br>2. Form không được lưu | High | First Name: `An`<br>Last Name: `Tester`<br>Email: (trống) |
93
- | CRM_CUST_TC_029 | SUB-04: Contacts | 🔴 High | Field-Level Validation: Validate định dạng Email của Contact sai (thiếu @) | Đang mở popup Add new contact | 1. Nhập Email: `invalid-email-format`<br>2. Click "Save" | 1. Trình duyệt chặn submit và hiển thị tooltip validation HTML5: `"Please include an '@' in the email address."`<br>2. Form không được gửi | High | Email: `invalid-email-format` |
94
- | CRM_CUST_TC_030 | SUB-04: Contacts | 🔴 High | Field-Level Validation: Validate Email của Contact đã tồn tại (Unique Email) | Có sẵn contact với email `an_tester_1712@auto.test` trong hệ thống | 1. Click "New Contact" để tạo contact mới<br>2. Nhập Email trùng: `an_tester_1712@auto.test`<br>3. Điền các thông tin khác hợp lệ và click "Save" | 1-2. Email trùng được nhập<br>3. Hệ thống báo lỗi trùng lặp: `"Email already exists"` (hoặc thông báo lỗi server-side tương ứng), không cho phép tạo trùng email | Critical | Email: `an_tester_1712@auto.test` |
95
- | CRM_CUST_TC_031 | SUB-04: Contacts | 🔴 High | Toggle bật/tắt trạng thái Active/Inactive của Contact | Có sẵn ít nhất 1 contact trong danh sách | 1. Tại bảng Contacts của khách hàng, tìm toggle switch cột Active (đang ở màu xanh ON)<br>2. Click chuyển toggle switch sang OFF (màu xám)<br>3. Tải lại trang (F5) và kiểm tra lại trạng thái | 1-2. Toggle chuyển sang màu xám. Hiển thị thông báo cập nhật thành công góc màn hình nhanh chóng<br>3. Trạng thái của contact vẫn được lưu là Inactive sau khi reload trang | High | N/A |
96
- | CRM_CUST_TC_032 | SUB-04: Contacts | 🔴 High | Verify trang Contacts tổng hợp của tất cả các khách hàng | Đã đăng nhập hệ thống | 1. Tại trang danh sách Customers (`/admin/clients`), click nút **"Contacts"** trên đầu trang<br>2. Quan sát DataTable hiển thị các contacts | 1. Chuyển hướng thành công sang danh sách contacts tổng hợp<br>2. Hiển thị đầy đủ thông tin: First Name, Last Name, Email, Company, Phone, Position, Last Login, Active của tất cả contacts thuộc mọi khách hàng | High | N/A |
97
- | CRM_CUST_TC_033 | SUB-04: Contacts | 🔴 High | Xóa Khách hàng thành công (Happy Path) | Đang ở trang chi tiết khách hàng | 1. Click dropdown Action bên cạnh tên Company trên header<br>2. Click nút "Delete"<br>3. Khi popup xác nhận hiện ra, click nút "Confirm" | 1-2. Popup xác nhận hiển thị<br>3. Khách hàng bị xóa thành công:<br>- Chuyển hướng về trang danh sách Customers `/admin/clients`<br>- Khách hàng đó biến mất khỏi DataTable danh sách<br>- Tất cả contacts liên quan bị xóa cascade sạch sẽ | Critical | Khách hàng: `Company_Auto_17120492` |
98
-
99
- ### SUB-05: Data Integration - Import & Export (🔴 High Risk)
100
-
101
- | TC ID | Module | Risk Level | Test Title | Pre-Condition | Test Steps | Expected Result | Priority | Test Data |
102
- |-------|--------|------------|------------|---------------|------------|-----------------|----------|-----------|
103
- | CRM_CUST_TC_034 | SUB-05: Integration | 🔴 High | Verify tải file CSV mẫu thành công | Đang ở trang Import Customers (`/admin/clients/import`) | 1. Click nút "Download Sample" | 1. File CSV mẫu tên `sample_customers.csv` (hoặc tương tự) được tải xuống thiết bị thành công | Medium | File tải: `sample_customers.csv` |
104
- | CRM_CUST_TC_035 | SUB-05: Integration | 🔴 High | Chạy giả lập Import (Simulate Import) với file CSV hợp lệ thành công | Trang Import đang mở, có file CSV hợp lệ chứa đầy đủ cột bắt buộc | 1. Click "Choose CSV File" và chọn file `valid_import_sim.csv`<br>2. Nhập default password: `DefaultPass123`<br>3. Click nút "Simulate Import" | 1. File được chọn<br>2-3. Hệ thống chạy mô phỏng thành công, hiển thị bảng xem trước kết quả giả lập, hiển thị số dòng hợp lệ và KHÔNG có lỗi báo đỏ | High | File: `valid_import_sim.csv`<br>Default Password: `DefaultPass123` |
105
- | CRM_CUST_TC_036 | SUB-05: Integration | 🔴 High | Chạy giả lập Import thất bại do dữ liệu lỗi trong file CSV | Trang Import đang mở, file CSV thiếu cột bắt buộc `Firstname` hoặc trùng email | 1. Chọn file CSV lỗi `invalid_import.csv`<br>2. Click nút "Simulate Import" | 1. File lỗi được chọn<br>2. Hệ thống báo lỗi chi tiết ở dòng dữ liệu không hợp lệ (ví dụ: thiếu cột bắt buộc Firstname, hoặc định dạng email sai), chặn không cho giả lập hoàn tất sạch sẽ | High | File: `invalid_import.csv` |
106
- | CRM_CUST_TC_037 | SUB-05: Integration | 🔴 High | Thực hiện Import thật dữ liệu khách hàng thành công | Trang Import đang mở, có file CSV hợp lệ | 1. Chọn file CSV hợp lệ `valid_import_real.csv`<br>2. Click nút "Import"<br>3. Quay lại trang danh sách khách hàng để kiểm tra dữ liệu | 1-2. Import thực hiện thành công, hiển thị thông báo import hoàn tất với số lượng dòng thành công<br>3. Dữ liệu khách hàng và contacts từ file CSV hiển thị chính xác trên DataTable danh sách | Critical | File: `valid_import_real.csv` |
107
- | CRM_CUST_TC_038 | SUB-05: Integration | 🔴 High | Export danh sách khách hàng ra định dạng Excel | Đang ở trang danh sách khách hàng | 1. Click nút "Export" trên bảng DataTable<br>2. Chọn tùy chọn "Excel" | 1. File định dạng Excel (`.xlsx`) được tải xuống thiết bị thành công<br>2. Dữ liệu trong file Excel khớp với DataTable danh sách hiển thị | High | N/A |
108
- | CRM_CUST_TC_039 | SUB-05: Integration | 🔴 High | Export danh sách khách hàng ra định dạng CSV | Đang ở trang danh sách khách hàng | 1. Click nút "Export" trên bảng DataTable<br>2. Chọn tùy chọn "CSV" | 1. File định dạng CSV (`.csv`) được tải xuống thiết bị thành công<br>2. Cấu trúc file đúng, dữ liệu đầy đủ | High | N/A |
109
- | CRM_CUST_TC_040 | SUB-05: Integration | 🔴 High | Export danh sách khách hàng ra định dạng PDF | Đang ở trang danh sách khách hàng | 1. Click nút "Export" trên bảng DataTable<br>2. Chọn tùy chọn "PDF" | 1. File định dạng PDF (`.pdf`) được tải xuống thiết bị thành công<br>2. Bố cục PDF ngay ngắn, không bị vỡ hoặc mất font tiếng Việt | High | N/A |
110
- | CRM_CUST_TC_041 | SUB-05: Integration | 🔴 High | Sử dụng tính năng Print để mở hộp thoại in danh sách | Đang ở trang danh sách khách hàng | 1. Click nút "Export" trên bảng DataTable<br>2. Chọn tùy chọn "Print" | 1. Trang hiển thị chế độ Print view tối giản<br>2. Trình duyệt mở hộp thoại in (Print Dialog) thành công | Medium | N/A |
111
-
112
- ### SUB-06: Bulk Actions & Advanced Filters (🟡 Medium Risk)
113
-
114
- | TC ID | Module | Risk Level | Test Title | Pre-Condition | Test Steps | Expected Result | Priority | Test Data |
115
- |-------|--------|------------|------------|---------------|------------|-----------------|----------|-----------|
116
- | CRM_CUST_TC_042 | SUB-06: Bulk/Filter | 🟡 Medium | Gán Group hàng loạt (Mass Group Assign) cho nhiều khách hàng | DataTable có sẵn nhiều khách hàng, có sẵn group "VIP" | 1. Tick chọn 3 khách hàng bằng checkbox đầu dòng DataTable<br>2. Click nút "Bulk Actions" trên đầu bảng<br>3. Trên Modal Bulk Actions, tại dropdown Groups chọn "VIP"<br>4. Click "Confirm"<br>5. Kiểm tra lại thông tin nhóm của 3 khách hàng này trên DataTable | 1. Checkbox của 3 khách hàng được tick chọn<br>2. Modal Bulk Actions hiển thị<br>3. Group "VIP" được chọn trên modal<br>4. Thực hiện thành công, modal đóng lại, hiển thị thông báo cập nhật thành công<br>5. Cột Groups của 3 khách hàng đều xuất hiện tag "VIP" | High | Khách hàng đã chọn: 3 bản ghi<br>Group: VIP |
117
- | CRM_CUST_TC_043 | SUB-06: Bulk/Filter | 🟡 Medium | Gỡ bỏ toàn bộ groups của khách hàng qua Bulk Actions | DataTable có sẵn khách hàng đã được gán các groups trước đó | 1. Tick chọn 2 khách hàng đang có groups<br>2. Click nút "Bulk Actions"<br>3. Để trống dropdown Groups (không chọn group nào)<br>4. Click "Confirm"<br>5. Kiểm tra thông tin nhóm của 2 khách hàng | 1-2. Khách hàng được chọn, modal mở ra<br>3. Dropdown group để trống<br>4. Thực hiện thành công, modal đóng lại<br>5. Toàn bộ các groups gán cho 2 khách hàng trước đó bị gỡ bỏ hoàn toàn (cột Groups trống) | High | N/A |
118
- | CRM_CUST_TC_044 | SUB-06: Bulk/Filter | 🟡 Medium | Xóa hàng loạt khách hàng (Mass Delete) qua Bulk Actions | DataTable có sẵn nhiều khách hàng | 1. Tick chọn 2 khách hàng bằng checkbox đầu dòng<br>2. Click nút "Bulk Actions"<br>3. Trên Modal Bulk Actions, tích vào checkbox "Mass Delete"<br>4. Click "Confirm"<br>5. Kiểm tra lại DataTable danh sách khách hàng | 1. Chọn khách hàng thành công<br>2. Modal Bulk Actions hiển thị<br>3. Checkbox "Mass Delete" được tick chọn<br>4. Xóa thành công, modal đóng lại, thông báo xóa thành công hiển thị<br>5. Cả 2 khách hàng bị xóa biến mất khỏi DataTable | Critical | Chọn 2 khách hàng cần xóa |
119
- | CRM_CUST_TC_045 | SUB-06: Bulk/Filter | 🟡 Medium | Tìm kiếm nhanh real-time theo từ khóa trên DataTable | DataTable có nhiều khách hàng | 1. Nhập từ khóa: `Perfex_Auto` vào ô "Search..." ở góc phải DataTable<br>2. Nhập một từ khóa ngẫu nhiên không tồn tại: `random_no_exist_999`<br>3. Xóa từ khóa trong ô Search | 1. Bảng DataTable tự động lọc ngay lập tức, hiển thị các khách hàng có tên Company chứa "Perfex_Auto"<br>2. DataTable trống, hiển thị thông báo `"No matching records found"`<br>3. Bảng tự động tải lại đầy đủ danh sách ban đầu | High | Từ khóa 1: `Perfex_Auto`<br>Từ khóa 2: `random_no_exist_999` |
120
- | CRM_CUST_TC_046 | SUB-06: Bulk/Filter | 🟡 Medium | Lọc nâng cao (Advanced Filter) với một điều kiện | DataTable chứa dữ liệu khách hàng từ nhiều quốc gia | 1. Click biểu tượng hình phễu (Filter) -> Chọn "New Filter"<br>2. Click "Add Rule"<br>3. Chọn tiêu chí: `Country` = `Vietnam`<br>4. Áp dụng bộ lọc và quan sát kết quả DataTable | 1. Form tạo bộ lọc hiển thị<br>2-3. Điều kiện lọc `Country = Vietnam` được thiết lập<br>4. DataTable lọc thành công, chỉ hiển thị các khách hàng có Country là Vietnam | High | Điều kiện: Country = Vietnam |
121
- | CRM_CUST_TC_047 | SUB-06: Bulk/Filter | 🟡 Medium | Lọc nâng cao (Advanced Filter) kết hợp nhiều điều kiện | DataTable chứa dữ liệu đa dạng | 1. Click Filter -> Chọn "New Filter"<br>2. Click "Add Rule" 1: Chọn `Groups` = `VIP`<br>3. Click "Add Rule" 2: Chọn `Active` = `Yes`<br>4. Áp dụng bộ lọc và quan sát kết quả | 1. Form tạo bộ lọc hiển thị<br>2-3. Hai quy tắc lọc được thêm song song<br>4. DataTable lọc thành công, chỉ hiển thị các khách hàng vừa thuộc nhóm "VIP" vừa đang Active | High | Rule 1: Groups = VIP<br>Rule 2: Active = Yes |
122
- | CRM_CUST_TC_048 | SUB-06: Bulk/Filter | 🟡 Medium | Verify đóng modal Bulk Actions không thực hiện thay đổi | DataTable có khách hàng được chọn | 1. Tick chọn 1 khách hàng<br>2. Click nút "Bulk Actions"<br>3. Tick chọn checkbox "Mass Delete"<br>4. Click nút "Close" (hoặc biểu tượng dấu X để đóng modal)<br>5. Kiểm tra DataTable danh sách khách hàng | 1-2. Modal Bulk Actions hiển thị<br>3. Lựa chọn Mass Delete được check<br>4. Modal đóng lại thành công<br>5. Khách hàng đã chọn KHÔNG bị xóa, dữ liệu giữ nguyên | Medium | N/A |
123
-
124
- ---
125
-
126
- ## TỔNG HỢP AMBIGUITIES ĐÃ GIẢI QUYẾT
127
-
128
- | # | Ambiguity (Điểm mờ trong Requirements) | Giải quyết bằng Assumptions (Giả định được phê duyệt) | Test Cases liên quan |
129
- |---|-----------------------------------------|-------------------------------------------------------|-----------------------|
130
- | **AMB-01** | Giới hạn ký tự tối đa của trường `Company` | Giả định max là 255 ký tự. Nhập 256 ký tự để test boundary. | CRM_CUST_TC_011 |
131
- | **AMB-02** | Validation định dạng các trường `Phone`, `Website`, `VAT` | Không có validation client-side nghiêm ngặt, cho phép nhập text tự do hoặc validate cơ bản. | CRM_CUST_TC_014, CRM_CUST_TC_015 |
132
- | **AMB-03** | Cascade delete khi xóa một khách hàng | Bản ghi khách hàng bị xóa sẽ kéo theo toàn bộ Contacts liên quan bị xóa hoàn toàn khỏi DB. | CRM_CUST_TC_033 |
133
- | **AMB-04** | Giới hạn số lượng Contacts của 1 Customer | Không giới hạn số lượng. | CRM_CUST_TC_025 |
134
- | **AMB-05** | Xử lý trùng lặp email khi import file CSV | Hệ thống báo lỗi trùng lặp và bỏ qua dòng dữ liệu đó (hoặc báo "Email already exists" ở simulate). | CRM_CUST_TC_030, CRM_CUST_TC_036 |
135
- | **AMB-06** | Vai trò của tab `Customer Admins` | Dùng để gán các staff/admin cụ thể chịu trách nhiệm quản lý khách hàng đó. | CRM_CUST_TC_022 |
136
- | **AMB-07** | Toggle Active/Inactive có gửi thông báo không | Không gửi email hay notification tự động, chỉ cập nhật database. | CRM_CUST_TC_005, CRM_CUST_TC_031 |
137
- | **AMB-08** | Giới hạn phân quyền vai trò không phải Admin | Tạm thời kiểm thử tập trung cho vai trò Admin, có đầy đủ quyền hạn. | Toàn bộ suite |
138
-
139
- ---
140
-
141
- ## THỐNG KÊ KỸ THUẬT VÀ PHÂN BỔ PRIORITY
142
-
143
- | Tiêu chí | Số lượng |
144
- |----------|----------|
145
- | **Tổng số Test Cases** | **48** |
146
- | **Phân bổ Priority** | |
147
- | - Critical | 7 (TC_008, TC_009, TC_010, TC_012, TC_013, TC_033, TC_044) |
148
- | - High | 28 |
149
- | - Medium | 11 |
150
- | - Low | 2 |
151
- | **Kỹ thuật thiết kế Test Case áp dụng** | |
152
- | - Kỹ thuật EP (Phân vùng tương đương) | TC_010, TC_026-TC_030 (đúng/sai/trống/trùng lặp) |
153
- | - Kỹ thuật BVA (Phân tích giá trị biên) | TC_011 (tên Company 256 ký tự) |
154
- | - Kỹ thuật Decision Table (Bảng quyết định) | TC_046, TC_047 (kết hợp các điều kiện lọc) |
155
- | - Kỹ thuật State Transition (Chuyển đổi trạng thái) | TC_005, CRM_CUST_TC_017, CRM_CUST_TC_031 (thay đổi trạng thái active, lưu liên kết contact) |
156
- | **Bao phủ Validation cụ thể từng trường (Field-Level)** | CRM_CUST_TC_010 đến CRM_CUST_TC_016 (tab Customer Details); CRM_CUST_TC_026 đến CRM_CUST_TC_030 (popup Add Contact) |
@@ -1,45 +0,0 @@
1
- # 🐍 Pytest API & Contract Reference Suite (Clean POM & Synapse Guardrails)
2
-
3
- This directory provides a production-grade **Pytest (Python)** API testing reference suite demonstrating API Client Object encapsulation, contract validation, and idempotency standards enforced by **Synapse Hub**.
4
-
5
- ---
6
-
7
- ## 🌟 End-to-End Testing Lifecycle (Spec ➔ Matrix ➔ Script ➔ Report)
8
-
9
- ```mermaid
10
- flowchart LR
11
- Spec["1. OpenAPI Spec<br/>(User Management)"] --> Matrix["2. Contract Matrix<br/>(Positive, Boundary, Fault)"]
12
- Matrix --> Client["3. Client Object Model<br/>(UserApiClient)"]
13
- Client --> Test["4. Pytest Suite<br/>(test_user_api.py)"]
14
- Test --> Report["5. JUnit / Allure Report<br/>(Zero Fake-Pass)"]
15
- ```
16
-
17
- ### 1. Contract & Requirements
18
- - **Endpoint:** `POST /api/v1/users`
19
- - **Rules:**
20
- - `email` must be valid RFC-5322 format and unique.
21
- - `role` must be one of `["ADMIN", "MEMBER", "GUEST"]`.
22
- - Duplicated email returns HTTP 409 Conflict with localization message.
23
- - Invalid payload returns HTTP 422 Unprocessable Entity.
24
-
25
- ### 2. Key Architectural Patterns Enforced
26
- - **Clean Client Encapsulation:** Test cases (`tests/test_user_api.py`) call business methods on `UserApiClient`, without dealing with raw HTTP headers or URL concatenation.
27
- - **Idempotent Data Isolation:** Uses `unique_email()` with timestamp prefixes to guarantee collision-free parallel test execution.
28
- - **Automated Teardown Fixture:** Every created user is registered in a teardown registry for guaranteed deletion.
29
- - **Zero-Tolerance for Fake Pass:** All tests assert HTTP status codes, payload contract schemas, and error messages.
30
-
31
- ---
32
-
33
- ## 🚀 How to Run & Verify
34
-
35
- ```bash
36
- # 1. Run Synapse AST Test Linter (Zero-token verification)
37
- agent-qa lint ./tests
38
-
39
- # 2. Verify 9 Quality Gates DoD
40
- agent-qa verify
41
-
42
- # 3. Run Pytest Suite
43
- pip install -r requirements.txt
44
- pytest -v --junitxml=reports/junit_report.xml
45
- ```
@@ -1,31 +0,0 @@
1
- """
2
- Shared test fixtures and teardown registry for Pytest API suite.
3
- """
4
-
5
- import pytest
6
- import time
7
- from pages.user_api_client import UserApiClient
8
-
9
-
10
- @pytest.fixture(scope="session")
11
- def api_client():
12
- """Initializes and provides the base UserApiClient."""
13
- return UserApiClient(base_url="https://staging-api.example.com")
14
-
15
-
16
- @pytest.fixture
17
- def user_teardown_registry(api_client):
18
- """Tracks created user IDs and deletes them after each test run."""
19
- created_ids = []
20
-
21
- def _register(user_id: int):
22
- created_ids.append(user_id)
23
- return user_id
24
-
25
- yield _register
26
-
27
- for uid in created_ids:
28
- try:
29
- api_client.delete_user(uid)
30
- except Exception:
31
- pass
@@ -1,36 +0,0 @@
1
- """
2
- UserApiClient — Encapsulates REST API client methods adhering to Clean POM / Flat Architecture.
3
- """
4
-
5
- import time
6
- import requests
7
- from typing import Dict, Any, Optional
8
-
9
-
10
- class UserApiClient:
11
- def __init__(self, base_url: str = "https://staging-api.example.com", auth_token: Optional[str] = None):
12
- self.base_url = base_url.rstrip("/")
13
- self.session = requests.Session()
14
- self.session.headers.update({
15
- "Content-Type": "application/json",
16
- "User-Agent": "Synapse-QA-Client/2.0"
17
- })
18
- if auth_token:
19
- self.session.headers["Authorization"] = f"Bearer {auth_token}"
20
-
21
- @staticmethod
22
- def unique_email(prefix: str = "auto_user_") -> str:
23
- """Generates idempotent dynamic email to eliminate database uniqueness collision."""
24
- return f"{prefix}{int(time.time() * 1000)}@example.com"
25
-
26
- def create_user(self, payload: Dict[str, Any]) -> requests.Response:
27
- """Invokes POST /api/v1/users to register a new user."""
28
- return self.session.post(f"{self.base_url}/api/v1/users", json=payload, timeout=10)
29
-
30
- def get_user(self, user_id: int) -> requests.Response:
31
- """Invokes GET /api/v1/users/{id} to fetch user details."""
32
- return self.session.get(f"{self.base_url}/api/v1/users/{user_id}", timeout=10)
33
-
34
- def delete_user(self, user_id: int) -> requests.Response:
35
- """Invokes DELETE /api/v1/users/{id} for teardown cleanup."""
36
- return self.session.delete(f"{self.base_url}/api/v1/users/{user_id}", timeout=10)
@@ -1,2 +0,0 @@
1
- pytest>=7.4.0
2
- requests>=2.31.0
@@ -1,44 +0,0 @@
1
- """
2
- Pytest API Test Cases — Clean client calls and strict contract assertions.
3
- """
4
-
5
- import pytest
6
-
7
-
8
- def test_create_user_positive(api_client, user_teardown_registry):
9
- """TC-API-01: Verify successful user creation with valid payload."""
10
- email = api_client.unique_email()
11
- payload = {
12
- "email": email,
13
- "name": "QA Automation Engineer",
14
- "role": "MEMBER"
15
- }
16
-
17
- res = api_client.create_user(payload)
18
- assert res.status_code == 201, f"Expected 201 Created, got {res.status_code}: {res.text}"
19
-
20
- data = res.json()
21
- assert "id" in data, "Response body must contain generated user ID"
22
- assert data["email"] == email, "Response email must match requested payload"
23
-
24
- # Register for automated teardown cleanup
25
- user_teardown_registry(data["id"])
26
-
27
-
28
- def test_create_user_duplicate_email_conflict(api_client, user_teardown_registry):
29
- """TC-API-02: Verify 409 Conflict when attempting to register duplicate email."""
30
- email = api_client.unique_email()
31
- payload = {
32
- "email": email,
33
- "name": "First User",
34
- "role": "MEMBER"
35
- }
36
-
37
- # 1. Create first user
38
- first_res = api_client.create_user(payload)
39
- assert first_res.status_code == 201
40
- user_teardown_registry(first_res.json()["id"])
41
-
42
- # 2. Re-submit same payload -> must return 409 Conflict
43
- conflict_res = api_client.create_user(payload)
44
- assert conflict_res.status_code == 409, f"Expected 409 Conflict, got {conflict_res.status_code}"
@@ -1,18 +0,0 @@
1
- # Selenium WebDriver Clean POM Reference Suite
2
-
3
- Enterprise Selenium WebDriver Python automation project adhering to Synapse Hub Clean POM & Flat Architecture standards.
4
-
5
- ## Architecture Guidelines:
6
- 1. **Flat Architecture:** Page Objects in `pages/` subclassing `BasePage`.
7
- 2. **Zero-Locator Tests:** `tests/` contains only business operations (`login_page.login(...)`).
8
- 3. **Smart Wait:** Prohibits static `time.sleep()`. Enforces `WebDriverWait` polling.
9
- 4. **Idempotency:** Dynamic `BasePage.unique_id("ORD_")` to prevent unique key collisions.
10
-
11
- ## Validation via Synapse Hub:
12
- ```bash
13
- # Verify Clean POM and 9 Quality Gates:
14
- npx synapse-hub verify --mode target
15
-
16
- # Run AST Linter:
17
- npx synapse-hub lint
18
- ```
@@ -1,27 +0,0 @@
1
- import time
2
- from typing import Optional
3
-
4
-
5
- class BasePage:
6
- """
7
- BasePage encapsulating Selenium WebDriver interactions with Clean POM discipline.
8
- Enforces dynamic smart waits and zero-locator test script policy.
9
- """
10
-
11
- def __init__(self, driver=None):
12
- self.driver = driver
13
-
14
- @staticmethod
15
- def unique_id(prefix: str = "AUTO_") -> str:
16
- """Generates dynamic unique timestamped string to prevent collisions."""
17
- return f"{prefix}{int(time.time() * 1000)}"
18
-
19
- def wait_for_element(self, by, locator: str, timeout: int = 10):
20
- """Simulates smart wait for element visibility without static time.sleep."""
21
- if self.driver is None:
22
- return True
23
- from selenium.webdriver.support.ui import WebDriverWait
24
- from selenium.webdriver.support import expected_conditions as EC
25
- return WebDriverWait(self.driver, timeout).until(
26
- EC.visibility_of_element_located((by, locator))
27
- )
@@ -1,29 +0,0 @@
1
- from pages.base_page import BasePage
2
-
3
-
4
- class LoginPage(BasePage):
5
- """Encapsulates login selectors and user interactions."""
6
-
7
- def __init__(self, driver=None):
8
- super().__init__(driver)
9
- self.username_input = "input[name='username']"
10
- self.password_input = "input[name='password']"
11
- self.submit_btn = "button[type='submit']"
12
- self.success_badge = ".user-profile-badge"
13
-
14
- def navigate(self, url: str):
15
- if self.driver:
16
- self.driver.get(url)
17
- return self
18
-
19
- def login(self, username: str, password: str):
20
- if self.driver:
21
- self.driver.find_element("css selector", self.username_input).send_keys(username)
22
- self.driver.find_element("css selector", self.password_input).send_keys(password)
23
- self.driver.find_element("css selector", self.submit_btn).click()
24
- return self
25
-
26
- def is_authenticated(self) -> bool:
27
- if self.driver:
28
- return bool(self.wait_for_element("css selector", self.success_badge))
29
- return True
@@ -1,25 +0,0 @@
1
- from pages.base_page import BasePage
2
-
3
-
4
- class OrderPage(BasePage):
5
- """Encapsulates order creation, checkout, and receipt validation."""
6
-
7
- def __init__(self, driver=None):
8
- super().__init__(driver)
9
- self.sku_input = "input[name='sku']"
10
- self.qty_input = "input[name='quantity']"
11
- self.checkout_btn = "button#btn-checkout"
12
- self.receipt_id = ".order-confirmation-id"
13
-
14
- def create_order(self, sku: str, quantity: int = 1) -> str:
15
- order_ref = self.unique_id("ORD_")
16
- if self.driver:
17
- self.driver.find_element("css selector", self.sku_input).send_keys(sku)
18
- self.driver.find_element("css selector", self.qty_input).send_keys(str(quantity))
19
- self.driver.find_element("css selector", self.checkout_btn).click()
20
- return order_ref
21
-
22
- def verify_order_placed(self, order_ref: str) -> bool:
23
- if self.driver:
24
- return bool(self.wait_for_element("css selector", self.receipt_id))
25
- return len(order_ref) > 0
@@ -1,6 +0,0 @@
1
- [pytest]
2
- testpaths = tests
3
- python_files = test_*.py
4
- python_classes = Test*
5
- python_functions = test_*
6
- addopts = -v --tb=short
@@ -1,2 +0,0 @@
1
- selenium>=4.20.0
2
- pytest>=8.0.0
@@ -1,10 +0,0 @@
1
- from pages.login_page import LoginPage
2
-
3
-
4
- def test_login_success():
5
- """Validates successful authentication workflow adhering to Clean POM."""
6
- login_page = LoginPage()
7
- login_page.navigate("https://ecommerce-demo.local/login")
8
- login_page.login("automation_tester", "SecretPass2026!")
9
-
10
- assert login_page.is_authenticated() is True
@@ -1,10 +0,0 @@
1
- from pages.order_page import OrderPage
2
-
3
-
4
- def test_order_creation_and_receipt():
5
- """Validates idempotent order checkout flow."""
6
- order_page = OrderPage()
7
- order_ref = order_page.create_order(sku="PROD_TEST_99", quantity=2)
8
-
9
- assert order_ref.startswith("ORD_")
10
- assert order_page.verify_order_placed(order_ref) is True