synapse-hub 2.4.2 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/orchestrator.md +1 -0
- package/.agents/agents/pom-crafter.md +1 -0
- package/.agents/agents/quality-gatekeeper.md +6 -0
- package/.agents/rules/skill-precedence.md +38 -18
- package/.agents/rules/systematic-debugging.md +2 -0
- package/.agents/skills/lean-code-ladder/SKILL.md +12 -1
- package/.agents/skills/lean-impact-scoreboard/SKILL.md +48 -0
- package/.agents/skills/lean-impact-scoreboard/examples/sample_benchmark_gain.md +20 -0
- package/.agents/skills/lean-impact-scoreboard/examples/sample_scoreboard.md +15 -0
- package/.agents/skills/repo-quality-audit/SKILL.md +68 -0
- package/.agents/skills/repo-quality-audit/examples/sample_audit_walkthrough.md +15 -0
- package/.agents/skills/repo-quality-audit/examples/sample_repo_audit.md +33 -0
- package/.agents/skills/senior-code-review/SKILL.md +90 -0
- package/.agents/skills/senior-code-review/examples/sample_review_walkthrough.md +29 -0
- package/.agents/skills/technical-debt-harvester/SKILL.md +70 -0
- package/.agents/skills/technical-debt-harvester/examples/sample_debt_ledger.md +25 -0
- package/.agents/skills/technical-debt-harvester/examples/sample_ledger_output.md +20 -0
- package/.cursor/rules/synapse-qa.mdc +4 -3
- package/.cursorrules +1 -0
- package/.windsurfrules +1 -0
- package/AGENTS.md +51 -179
- package/CLAUDE.md +5 -0
- package/README.en.md +12 -8
- package/README.md +12 -8
- package/bin/agent-qa.js +3 -2
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/CLI_REFERENCE.md +15 -1
- package/package.json +1 -3
- package/skills_manifest.json +38 -2
- package/synapse.config.json +1 -1
- package/templates/ai_bridges/CLAUDE.md +5 -0
- package/templates/ai_bridges/copilot-instructions.md +2 -0
- package/templates/ai_bridges/cursor_synapse_qa.mdc +4 -3
- package/templates/ai_bridges/windsurfrules +1 -0
- package/tools/.infer_baseline.json +7 -1079
- package/tools/TOOLS_RUNBOOK.md +53 -13
- package/tools/__init__.py +1 -1
- package/tools/agent_canary.py +4 -4
- package/tools/analyze_project.py +2 -2
- package/tools/api_fuzzer.py +4 -4
- package/tools/auto_heal.py +4 -2
- package/tools/browser_helpers.js +2 -2
- package/tools/bump_version.py +30 -2
- package/tools/cache_resolver.py +6 -6
- package/tools/check_env.py +82 -18
- package/tools/cli.py +265 -197
- package/tools/count_tokens.py +7 -7
- package/tools/dashboard_generator.py +14 -14
- package/tools/db_sandbox.py +5 -5
- package/tools/debt_harvester.py +236 -0
- package/tools/distill_log.py +2 -2
- package/tools/flaky_detector.py +5 -5
- package/tools/generate_tc_from_story.py +2 -2
- package/tools/generate_test_reports.py +4 -4
- package/tools/git_pre_commit_guard.py +6 -6
- package/tools/infer_linter.py +12 -7
- package/tools/lint_ai_docs.py +2 -2
- package/tools/matrix_sharder.py +4 -4
- package/tools/mcp_excel_server.py +1 -1
- package/tools/mcp_server.py +3 -3
- package/tools/micro_mutator.py +6 -6
- package/tools/omni_transpiler.py +2 -2
- package/tools/output_formatter.py +37 -2
- package/tools/pinpoint_failure.py +4 -1
- package/tools/repo_map.py +2 -2
- package/tools/safety_firewall.py +2 -2
- package/tools/senior_code_review.py +341 -0
- package/tools/setup_project.py +17 -9
- package/tools/skillspector.py +2 -2
- package/tools/sync_agent_rules.py +14 -3
- package/tools/test_impact.py +4 -4
- package/tools/tms_sync_bridge.py +2 -2
- package/tools/update_sentinel.py +24 -23
- package/tools/verify_all.py +12 -12
- package/tools/visual_som_helper.py +8 -8
- package/tools/watch_mode.py +6 -7
- package/tools/wcag_audit.py +5 -1
- package/tools/welcome_page.py +11 -11
- package/benchmarks/qa_eval/README.md +0 -71
- package/benchmarks/qa_eval/build_dataset.py +0 -677
- package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-06_api_contract_drift.json +0 -22
- package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-07_api_contract_drift.json +0 -22
- package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-08_api_contract_drift.json +0 -22
- package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-09_api_contract_drift.json +0 -22
- package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-10_api_contract_drift.json +0 -22
- package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-21_fake_pass_tautology.json +0 -21
- package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-22_fake_pass_tautology.json +0 -21
- package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-23_fake_pass_tautology.json +0 -21
- package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-24_fake_pass_tautology.json +0 -21
- package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-25_fake_pass_tautology.json +0 -21
- package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-01_locator_drift.json +0 -23
- package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-02_locator_drift.json +0 -23
- package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-03_locator_drift.json +0 -23
- package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-04_locator_drift.json +0 -23
- package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-05_locator_drift.json +0 -23
- package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-16_stale_and_mutation.json +0 -22
- package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-17_stale_and_mutation.json +0 -22
- package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-18_stale_and_mutation.json +0 -22
- package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-19_stale_and_mutation.json +0 -22
- package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-20_stale_and_mutation.json +0 -23
- package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-11_timing_and_race.json +0 -22
- package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-12_timing_and_race.json +0 -22
- package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-13_timing_and_race.json +0 -22
- package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-14_timing_and_race.json +0 -22
- package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-15_timing_and_race.json +0 -22
- package/benchmarks/qa_eval/eval_runner.py +0 -418
- package/benchmarks/qa_eval/reports/BENCHMARK_REPORT.md +0 -94
- package/benchmarks/qa_eval/reports/eval_results.json +0 -461
- package/examples/cypress-ecommerce-suite/README.md +0 -45
- package/examples/cypress-ecommerce-suite/cypress/e2e/checkout.cy.js +0 -17
- package/examples/cypress-ecommerce-suite/cypress/pages/BasePage.js +0 -29
- package/examples/cypress-ecommerce-suite/cypress/pages/CartPage.js +0 -51
- package/examples/cypress-ecommerce-suite/cypress.config.js +0 -13
- package/examples/cypress-ecommerce-suite/package.json +0 -13
- package/examples/katalon-suite/Keywords/pages/BasePage.groovy +0 -19
- package/examples/katalon-suite/Keywords/pages/DashboardPage.groovy +0 -29
- package/examples/katalon-suite/Keywords/pages/LoginPage.groovy +0 -45
- package/examples/katalon-suite/Profiles/default.glbl +0 -12
- package/examples/katalon-suite/README.md +0 -18
- package/examples/katalon-suite/Scripts/Auth/TC_LoginSuccess/Script1234567890.groovy +0 -12
- package/examples/katalon-suite/Scripts/Dashboard/TC_ViewSummary/Script1234567891.groovy +0 -15
- package/examples/katalon-suite/katalon_suite.prj +0 -21
- package/examples/playwright-crm-suite/README.md +0 -40
- package/examples/playwright-crm-suite/package.json +0 -14
- package/examples/playwright-crm-suite/pages/BasePage.ts +0 -37
- package/examples/playwright-crm-suite/pages/CustomerPage.ts +0 -57
- package/examples/playwright-crm-suite/pages/LoginPage.ts +0 -32
- package/examples/playwright-crm-suite/playwright.config.ts +0 -27
- package/examples/playwright-crm-suite/tests/auth.spec.ts +0 -22
- package/examples/playwright-crm-suite/tests/customers.spec.ts +0 -33
- package/examples/plugins/sample-custom-plugin/README.md +0 -14
- package/examples/plugins/sample-custom-plugin/plugin.json +0 -16
- package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/SKILL.md +0 -29
- package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/examples/audit_fixture.json +0 -8
- package/examples/plugins/sample-custom-plugin/tools/custom_security_headers.py +0 -125
- package/examples/practices/README.md +0 -22
- package/examples/practices/requirements/crm/requirements_contracts.md +0 -292
- package/examples/practices/requirements/crm/requirements_customers.md +0 -462
- package/examples/practices/requirements/crm/requirements_login.md +0 -202
- package/examples/practices/requirements/crm/requirements_projects.md +0 -396
- package/examples/practices/requirements/crm/requirements_tasks.md +0 -343
- package/examples/practices/requirements/ecommerce/requirements_category_brand.txt +0 -402
- package/examples/practices/requirements/jira/KAN-5/KAN-1/2026-04-03_04-28-27.png +0 -0
- package/examples/practices/requirements/jira/KAN-5/KAN-1/KAN-1_requirement.md +0 -52
- package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-19.png +0 -0
- package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-57.png +0 -0
- package/examples/practices/requirements/jira/KAN-5/KAN-4/KAN-4_requirement.md +0 -119
- package/examples/practices/requirements/jira/KAN-5/KAN-5_overview.md +0 -9
- package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-08.png +0 -0
- package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-23.png +0 -0
- package/examples/practices/requirements/jira/KAN-5/KAN-6/KAN-6_requirement.md +0 -32
- package/examples/practices/testcases/api/book_management_api_test_cases.md +0 -144
- package/examples/practices/testcases/api/user_management_api_test_cases.md +0 -139
- package/examples/practices/testcases/crm/test_cases_crm_login.md +0 -148
- package/examples/practices/testcases/crm/test_cases_customers.md +0 -156
- package/examples/pytest-api-suite/README.md +0 -45
- package/examples/pytest-api-suite/conftest.py +0 -31
- package/examples/pytest-api-suite/pages/user_api_client.py +0 -36
- package/examples/pytest-api-suite/requirements.txt +0 -2
- package/examples/pytest-api-suite/tests/test_user_api.py +0 -44
- package/examples/selenium-suite/README.md +0 -18
- package/examples/selenium-suite/pages/base_page.py +0 -27
- package/examples/selenium-suite/pages/login_page.py +0 -29
- package/examples/selenium-suite/pages/order_page.py +0 -25
- package/examples/selenium-suite/pytest.ini +0 -6
- package/examples/selenium-suite/requirements.txt +0 -2
- package/examples/selenium-suite/tests/test_login.py +0 -10
- package/examples/selenium-suite/tests/test_order.py +0 -10
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"timestamp": "2026-11-01T21:24:43.176168",
|
|
3
|
-
"total_challenges": 25,
|
|
4
|
-
"passed_challenges": 25,
|
|
5
|
-
"failed_challenges": 0,
|
|
6
|
-
"resolution_rate_pct": 100.0,
|
|
7
|
-
"avg_mttr_sec": 0.0,
|
|
8
|
-
"avg_token_consumption": 76.2,
|
|
9
|
-
"avg_mutation_score": 100.0,
|
|
10
|
-
"zero_side_effect_rate_pct": 100.0,
|
|
11
|
-
"safety_firewall_clearance_pct": 100.0,
|
|
12
|
-
"categories": {
|
|
13
|
-
"api_contract_drift": {
|
|
14
|
-
"total": 5,
|
|
15
|
-
"passed": 5,
|
|
16
|
-
"tokens": 389,
|
|
17
|
-
"time": 0.0001,
|
|
18
|
-
"resolution_rate": 100.0,
|
|
19
|
-
"avg_tokens": 77.8,
|
|
20
|
-
"avg_time": 0.0
|
|
21
|
-
},
|
|
22
|
-
"fake_pass_tautology": {
|
|
23
|
-
"total": 5,
|
|
24
|
-
"passed": 5,
|
|
25
|
-
"tokens": 335,
|
|
26
|
-
"time": 0.0004,
|
|
27
|
-
"resolution_rate": 100.0,
|
|
28
|
-
"avg_tokens": 67.0,
|
|
29
|
-
"avg_time": 0.0
|
|
30
|
-
},
|
|
31
|
-
"locator_drift": {
|
|
32
|
-
"total": 5,
|
|
33
|
-
"passed": 5,
|
|
34
|
-
"tokens": 262,
|
|
35
|
-
"time": 0.0002,
|
|
36
|
-
"resolution_rate": 100.0,
|
|
37
|
-
"avg_tokens": 52.4,
|
|
38
|
-
"avg_time": 0.0
|
|
39
|
-
},
|
|
40
|
-
"stale_and_mutation": {
|
|
41
|
-
"total": 5,
|
|
42
|
-
"passed": 5,
|
|
43
|
-
"tokens": 473,
|
|
44
|
-
"time": 0.0,
|
|
45
|
-
"resolution_rate": 100.0,
|
|
46
|
-
"avg_tokens": 94.6,
|
|
47
|
-
"avg_time": 0.0
|
|
48
|
-
},
|
|
49
|
-
"timing_and_race": {
|
|
50
|
-
"total": 5,
|
|
51
|
-
"passed": 5,
|
|
52
|
-
"tokens": 447,
|
|
53
|
-
"time": 0.0,
|
|
54
|
-
"resolution_rate": 100.0,
|
|
55
|
-
"avg_tokens": 89.4,
|
|
56
|
-
"avg_time": 0.0
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
"details": [
|
|
60
|
-
{
|
|
61
|
-
"id": "QA-CHALLENGE-06",
|
|
62
|
-
"name": "Numeric ID Serialized as String (Type Mutation)",
|
|
63
|
-
"category": "api_contract_drift",
|
|
64
|
-
"difficulty": "MEDIUM",
|
|
65
|
-
"status": "PASSED",
|
|
66
|
-
"resolved": true,
|
|
67
|
-
"safety_cleared": true,
|
|
68
|
-
"time_to_heal_sec": 0.0001,
|
|
69
|
-
"max_time_allowed_sec": 2.0,
|
|
70
|
-
"time_compliant": true,
|
|
71
|
-
"token_consumption": 77,
|
|
72
|
-
"mutation_score": 100.0,
|
|
73
|
-
"zero_side_effect": true,
|
|
74
|
-
"resolution_notes": "Updated assertion contract: assert str(response.data.invoice_id) == '10423'"
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
"id": "QA-CHALLENGE-07",
|
|
78
|
-
"name": "Payload Response Field Rename",
|
|
79
|
-
"category": "api_contract_drift",
|
|
80
|
-
"difficulty": "EASY",
|
|
81
|
-
"status": "PASSED",
|
|
82
|
-
"resolved": true,
|
|
83
|
-
"safety_cleared": true,
|
|
84
|
-
"time_to_heal_sec": 0.0,
|
|
85
|
-
"max_time_allowed_sec": 1.5,
|
|
86
|
-
"time_compliant": true,
|
|
87
|
-
"token_consumption": 91,
|
|
88
|
-
"mutation_score": 100.0,
|
|
89
|
-
"zero_side_effect": true,
|
|
90
|
-
"resolution_notes": "Updated assertion contract: assert (response.json().get('email_address') or response.json().get('user_email')) == 'qa@example.com'"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"id": "QA-CHALLENGE-08",
|
|
94
|
-
"name": "Nested Response Object Flattened",
|
|
95
|
-
"category": "api_contract_drift",
|
|
96
|
-
"difficulty": "MEDIUM",
|
|
97
|
-
"status": "PASSED",
|
|
98
|
-
"resolved": true,
|
|
99
|
-
"safety_cleared": true,
|
|
100
|
-
"time_to_heal_sec": 0.0,
|
|
101
|
-
"max_time_allowed_sec": 2.0,
|
|
102
|
-
"time_compliant": true,
|
|
103
|
-
"token_consumption": 82,
|
|
104
|
-
"mutation_score": 100.0,
|
|
105
|
-
"zero_side_effect": true,
|
|
106
|
-
"resolution_notes": "Updated assertion contract: tier = response.json().get('tier') or response.json().get('profile', {}).get('tier'); assert tier == 'GOLD'"
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"id": "QA-CHALLENGE-09",
|
|
110
|
-
"name": "HTTP Creation Status Code Shift (200 OK -> 201 Created)",
|
|
111
|
-
"category": "api_contract_drift",
|
|
112
|
-
"difficulty": "EASY",
|
|
113
|
-
"status": "PASSED",
|
|
114
|
-
"resolved": true,
|
|
115
|
-
"safety_cleared": true,
|
|
116
|
-
"time_to_heal_sec": 0.0,
|
|
117
|
-
"max_time_allowed_sec": 1.0,
|
|
118
|
-
"time_compliant": true,
|
|
119
|
-
"token_consumption": 64,
|
|
120
|
-
"mutation_score": 100.0,
|
|
121
|
-
"zero_side_effect": true,
|
|
122
|
-
"resolution_notes": "Updated assertion contract: assert response.status_code in (200, 201)"
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"id": "QA-CHALLENGE-10",
|
|
126
|
-
"name": "Authentication Header Prefix Upgrade",
|
|
127
|
-
"category": "api_contract_drift",
|
|
128
|
-
"difficulty": "EASY",
|
|
129
|
-
"status": "PASSED",
|
|
130
|
-
"resolved": true,
|
|
131
|
-
"safety_cleared": true,
|
|
132
|
-
"time_to_heal_sec": 0.0,
|
|
133
|
-
"max_time_allowed_sec": 1.5,
|
|
134
|
-
"time_compliant": true,
|
|
135
|
-
"token_consumption": 75,
|
|
136
|
-
"mutation_score": 100.0,
|
|
137
|
-
"zero_side_effect": true,
|
|
138
|
-
"resolution_notes": "Updated assertion contract: headers = {'Authorization': f'Bearer {auth_token}'}"
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"id": "QA-CHALLENGE-21",
|
|
142
|
-
"name": "Swallowed HTTP 500 Error in Empty Catch Block",
|
|
143
|
-
"category": "fake_pass_tautology",
|
|
144
|
-
"difficulty": "EASY",
|
|
145
|
-
"status": "PASSED",
|
|
146
|
-
"resolved": true,
|
|
147
|
-
"safety_cleared": true,
|
|
148
|
-
"time_to_heal_sec": 0.0002,
|
|
149
|
-
"max_time_allowed_sec": 1.0,
|
|
150
|
-
"time_compliant": true,
|
|
151
|
-
"token_consumption": 65,
|
|
152
|
-
"mutation_score": 100.0,
|
|
153
|
-
"zero_side_effect": true,
|
|
154
|
-
"resolution_notes": "Injected robust assertion (Mutation Score: 100.0%): REMOVE_SWALLOWED_CATCH_AND_ASSERT"
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
"id": "QA-CHALLENGE-22",
|
|
158
|
-
"name": "Zero-Assertion Smoke Test (Tautological Pass)",
|
|
159
|
-
"category": "fake_pass_tautology",
|
|
160
|
-
"difficulty": "EASY",
|
|
161
|
-
"status": "PASSED",
|
|
162
|
-
"resolved": true,
|
|
163
|
-
"safety_cleared": true,
|
|
164
|
-
"time_to_heal_sec": 0.0001,
|
|
165
|
-
"max_time_allowed_sec": 1.5,
|
|
166
|
-
"time_compliant": true,
|
|
167
|
-
"token_consumption": 84,
|
|
168
|
-
"mutation_score": 100.0,
|
|
169
|
-
"zero_side_effect": true,
|
|
170
|
-
"resolution_notes": "Injected robust assertion (Mutation Score: 100.0%): INJECT_POST_CONDITION_ASSERTION"
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
"id": "QA-CHALLENGE-23",
|
|
174
|
-
"name": "Tautological assert true == true",
|
|
175
|
-
"category": "fake_pass_tautology",
|
|
176
|
-
"difficulty": "EASY",
|
|
177
|
-
"status": "PASSED",
|
|
178
|
-
"resolved": true,
|
|
179
|
-
"safety_cleared": true,
|
|
180
|
-
"time_to_heal_sec": 0.0001,
|
|
181
|
-
"max_time_allowed_sec": 1.0,
|
|
182
|
-
"time_compliant": true,
|
|
183
|
-
"token_consumption": 58,
|
|
184
|
-
"mutation_score": 100.0,
|
|
185
|
-
"zero_side_effect": true,
|
|
186
|
-
"resolution_notes": "Injected robust assertion (Mutation Score: 100.0%): MUTATION_TESTING_KILLS_TAUTOLOGY"
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
"id": "QA-CHALLENGE-24",
|
|
190
|
-
"name": "Unawaited Async Assertion (Silent Green Trap)",
|
|
191
|
-
"category": "fake_pass_tautology",
|
|
192
|
-
"difficulty": "MEDIUM",
|
|
193
|
-
"status": "PASSED",
|
|
194
|
-
"resolved": true,
|
|
195
|
-
"safety_cleared": true,
|
|
196
|
-
"time_to_heal_sec": 0.0,
|
|
197
|
-
"max_time_allowed_sec": 1.5,
|
|
198
|
-
"time_compliant": true,
|
|
199
|
-
"token_consumption": 64,
|
|
200
|
-
"mutation_score": 100.0,
|
|
201
|
-
"zero_side_effect": true,
|
|
202
|
-
"resolution_notes": "Injected robust assertion (Mutation Score: 100.0%): AST_ASYNC_AWAIT_ENFORCEMENT"
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
"id": "QA-CHALLENGE-25",
|
|
206
|
-
"name": "Unbounded Relational Assertion (status >= 200)",
|
|
207
|
-
"category": "fake_pass_tautology",
|
|
208
|
-
"difficulty": "EASY",
|
|
209
|
-
"status": "PASSED",
|
|
210
|
-
"resolved": true,
|
|
211
|
-
"safety_cleared": true,
|
|
212
|
-
"time_to_heal_sec": 0.0,
|
|
213
|
-
"max_time_allowed_sec": 1.2,
|
|
214
|
-
"time_compliant": true,
|
|
215
|
-
"token_consumption": 64,
|
|
216
|
-
"mutation_score": 100.0,
|
|
217
|
-
"zero_side_effect": true,
|
|
218
|
-
"resolution_notes": "Injected robust assertion (Mutation Score: 100.0%): BOUNDED_INTERVAL_ASSERTION"
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
"id": "QA-CHALLENGE-01",
|
|
222
|
-
"name": "Dynamic CSS-in-JS Hash Locator Shift",
|
|
223
|
-
"category": "locator_drift",
|
|
224
|
-
"difficulty": "MEDIUM",
|
|
225
|
-
"status": "PASSED",
|
|
226
|
-
"resolved": true,
|
|
227
|
-
"safety_cleared": true,
|
|
228
|
-
"time_to_heal_sec": 0.0002,
|
|
229
|
-
"max_time_allowed_sec": 2.0,
|
|
230
|
-
"time_compliant": true,
|
|
231
|
-
"token_consumption": 50,
|
|
232
|
-
"mutation_score": 100.0,
|
|
233
|
-
"zero_side_effect": true,
|
|
234
|
-
"resolution_notes": "Autonomous cascade healed: 'button.btn-submit' -> '[data-testid='submit-order-btn']' (confidence 0.98)"
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
"id": "QA-CHALLENGE-02",
|
|
238
|
-
"name": "Nested Shadow DOM Component Piercing",
|
|
239
|
-
"category": "locator_drift",
|
|
240
|
-
"difficulty": "HARD",
|
|
241
|
-
"status": "PASSED",
|
|
242
|
-
"resolved": true,
|
|
243
|
-
"safety_cleared": true,
|
|
244
|
-
"time_to_heal_sec": 0.0,
|
|
245
|
-
"max_time_allowed_sec": 3.0,
|
|
246
|
-
"time_compliant": true,
|
|
247
|
-
"token_consumption": 62,
|
|
248
|
-
"mutation_score": 100.0,
|
|
249
|
-
"zero_side_effect": true,
|
|
250
|
-
"resolution_notes": "Autonomous cascade healed: '#customer-email' -> 'template:has-text('<input id='customer-email' type='email' data-testid='user-email'/>')' (confidence 0.94)"
|
|
251
|
-
},
|
|
252
|
-
{
|
|
253
|
-
"id": "QA-CHALLENGE-03",
|
|
254
|
-
"name": "Semantic Text Label Evolution",
|
|
255
|
-
"category": "locator_drift",
|
|
256
|
-
"difficulty": "EASY",
|
|
257
|
-
"status": "PASSED",
|
|
258
|
-
"resolved": true,
|
|
259
|
-
"safety_cleared": true,
|
|
260
|
-
"time_to_heal_sec": 0.0,
|
|
261
|
-
"max_time_allowed_sec": 1.5,
|
|
262
|
-
"time_compliant": true,
|
|
263
|
-
"token_consumption": 50,
|
|
264
|
-
"mutation_score": 100.0,
|
|
265
|
-
"zero_side_effect": true,
|
|
266
|
-
"resolution_notes": "Autonomous cascade healed: '//button[text()='Submit Order']' -> '#order-cta' (confidence 0.9)"
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
"id": "QA-CHALLENGE-04",
|
|
270
|
-
"name": "Legacy ID Removed in Favor of data-testid",
|
|
271
|
-
"category": "locator_drift",
|
|
272
|
-
"difficulty": "EASY",
|
|
273
|
-
"status": "PASSED",
|
|
274
|
-
"resolved": true,
|
|
275
|
-
"safety_cleared": true,
|
|
276
|
-
"time_to_heal_sec": 0.0,
|
|
277
|
-
"max_time_allowed_sec": 1.5,
|
|
278
|
-
"time_compliant": true,
|
|
279
|
-
"token_consumption": 50,
|
|
280
|
-
"mutation_score": 100.0,
|
|
281
|
-
"zero_side_effect": true,
|
|
282
|
-
"resolution_notes": "Autonomous cascade healed: '#customer_name' -> '[data-testid='customer-name-field']' (confidence 0.98)"
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
"id": "QA-CHALLENGE-05",
|
|
286
|
-
"name": "HTML Tag Evolution: Anchor to ARIA Button",
|
|
287
|
-
"category": "locator_drift",
|
|
288
|
-
"difficulty": "MEDIUM",
|
|
289
|
-
"status": "PASSED",
|
|
290
|
-
"resolved": true,
|
|
291
|
-
"safety_cleared": true,
|
|
292
|
-
"time_to_heal_sec": 0.0,
|
|
293
|
-
"max_time_allowed_sec": 2.0,
|
|
294
|
-
"time_compliant": true,
|
|
295
|
-
"token_consumption": 50,
|
|
296
|
-
"mutation_score": 100.0,
|
|
297
|
-
"zero_side_effect": true,
|
|
298
|
-
"resolution_notes": "Autonomous cascade healed: 'a.nav-export' -> 'button' (confidence 0.9)"
|
|
299
|
-
},
|
|
300
|
-
{
|
|
301
|
-
"id": "QA-CHALLENGE-16",
|
|
302
|
-
"name": "WebSocket Table Auto-Refresh Stale Reference",
|
|
303
|
-
"category": "stale_and_mutation",
|
|
304
|
-
"difficulty": "HARD",
|
|
305
|
-
"status": "PASSED",
|
|
306
|
-
"resolved": true,
|
|
307
|
-
"safety_cleared": true,
|
|
308
|
-
"time_to_heal_sec": 0.0,
|
|
309
|
-
"max_time_allowed_sec": 2.5,
|
|
310
|
-
"time_compliant": true,
|
|
311
|
-
"token_consumption": 88,
|
|
312
|
-
"mutation_score": 100.0,
|
|
313
|
-
"zero_side_effect": true,
|
|
314
|
-
"resolution_notes": "Eliminated stale element trap: DYNAMIC_LOCATOR_REQUERY"
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
"id": "QA-CHALLENGE-17",
|
|
318
|
-
"name": "Tab Switch Component Unmount Detach",
|
|
319
|
-
"category": "stale_and_mutation",
|
|
320
|
-
"difficulty": "MEDIUM",
|
|
321
|
-
"status": "PASSED",
|
|
322
|
-
"resolved": true,
|
|
323
|
-
"safety_cleared": true,
|
|
324
|
-
"time_to_heal_sec": 0.0,
|
|
325
|
-
"max_time_allowed_sec": 2.0,
|
|
326
|
-
"time_compliant": true,
|
|
327
|
-
"token_consumption": 103,
|
|
328
|
-
"mutation_score": 100.0,
|
|
329
|
-
"zero_side_effect": true,
|
|
330
|
-
"resolution_notes": "Eliminated stale element trap: TAB_MOUNT_SYNCHRONIZATION"
|
|
331
|
-
},
|
|
332
|
-
{
|
|
333
|
-
"id": "QA-CHALLENGE-18",
|
|
334
|
-
"name": "Pagination Replaces tbody (Stale Row Index)",
|
|
335
|
-
"category": "stale_and_mutation",
|
|
336
|
-
"difficulty": "MEDIUM",
|
|
337
|
-
"status": "PASSED",
|
|
338
|
-
"resolved": true,
|
|
339
|
-
"safety_cleared": true,
|
|
340
|
-
"time_to_heal_sec": 0.0,
|
|
341
|
-
"max_time_allowed_sec": 2.0,
|
|
342
|
-
"time_compliant": true,
|
|
343
|
-
"token_consumption": 97,
|
|
344
|
-
"mutation_score": 100.0,
|
|
345
|
-
"zero_side_effect": true,
|
|
346
|
-
"resolution_notes": "Eliminated stale element trap: PAGINATION_NETWORK_SYNC"
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
"id": "QA-CHALLENGE-19",
|
|
350
|
-
"name": "Reopened Modal Generates Fresh DOM Node",
|
|
351
|
-
"category": "stale_and_mutation",
|
|
352
|
-
"difficulty": "MEDIUM",
|
|
353
|
-
"status": "PASSED",
|
|
354
|
-
"resolved": true,
|
|
355
|
-
"safety_cleared": true,
|
|
356
|
-
"time_to_heal_sec": 0.0,
|
|
357
|
-
"max_time_allowed_sec": 2.0,
|
|
358
|
-
"time_compliant": true,
|
|
359
|
-
"token_consumption": 111,
|
|
360
|
-
"mutation_score": 100.0,
|
|
361
|
-
"zero_side_effect": true,
|
|
362
|
-
"resolution_notes": "Eliminated stale element trap: VISIBLE_SELECTOR_REBIND"
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
"id": "QA-CHALLENGE-20",
|
|
366
|
-
"name": "Locale Swap Invalidates Hardcoded Text Selectors",
|
|
367
|
-
"category": "stale_and_mutation",
|
|
368
|
-
"difficulty": "EASY",
|
|
369
|
-
"status": "PASSED",
|
|
370
|
-
"resolved": true,
|
|
371
|
-
"safety_cleared": true,
|
|
372
|
-
"time_to_heal_sec": 0.0,
|
|
373
|
-
"max_time_allowed_sec": 1.5,
|
|
374
|
-
"time_compliant": true,
|
|
375
|
-
"token_consumption": 74,
|
|
376
|
-
"mutation_score": 100.0,
|
|
377
|
-
"zero_side_effect": true,
|
|
378
|
-
"resolution_notes": "Eliminated stale element trap: LOCALE_AGNOSTIC_DATA_ATTRIBUTES"
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
"id": "QA-CHALLENGE-11",
|
|
382
|
-
"name": "Network Latency Spike (3,500ms Throttle) Breaking Static Sleep",
|
|
383
|
-
"category": "timing_and_race",
|
|
384
|
-
"difficulty": "MEDIUM",
|
|
385
|
-
"status": "PASSED",
|
|
386
|
-
"resolved": true,
|
|
387
|
-
"safety_cleared": true,
|
|
388
|
-
"time_to_heal_sec": 0.0,
|
|
389
|
-
"max_time_allowed_sec": 2.5,
|
|
390
|
-
"time_compliant": true,
|
|
391
|
-
"token_consumption": 88,
|
|
392
|
-
"mutation_score": 100.0,
|
|
393
|
-
"zero_side_effect": true,
|
|
394
|
-
"resolution_notes": "Replaced brittle timing with dynamic smart barrier: DYNAMIC_SMART_WAIT"
|
|
395
|
-
},
|
|
396
|
-
{
|
|
397
|
-
"id": "QA-CHALLENGE-12",
|
|
398
|
-
"name": "PrimeNG Modal Backdrop Fading Intercept",
|
|
399
|
-
"category": "timing_and_race",
|
|
400
|
-
"difficulty": "HARD",
|
|
401
|
-
"status": "PASSED",
|
|
402
|
-
"resolved": true,
|
|
403
|
-
"safety_cleared": true,
|
|
404
|
-
"time_to_heal_sec": 0.0,
|
|
405
|
-
"max_time_allowed_sec": 3.0,
|
|
406
|
-
"time_compliant": true,
|
|
407
|
-
"token_consumption": 91,
|
|
408
|
-
"mutation_score": 100.0,
|
|
409
|
-
"zero_side_effect": true,
|
|
410
|
-
"resolution_notes": "Replaced brittle timing with dynamic smart barrier: OVERLAY_SAFE_DISPATCH"
|
|
411
|
-
},
|
|
412
|
-
{
|
|
413
|
-
"id": "QA-CHALLENGE-13",
|
|
414
|
-
"name": "Debounced Search Input Autocomplete Race",
|
|
415
|
-
"category": "timing_and_race",
|
|
416
|
-
"difficulty": "MEDIUM",
|
|
417
|
-
"status": "PASSED",
|
|
418
|
-
"resolved": true,
|
|
419
|
-
"safety_cleared": true,
|
|
420
|
-
"time_to_heal_sec": 0.0,
|
|
421
|
-
"max_time_allowed_sec": 2.5,
|
|
422
|
-
"time_compliant": true,
|
|
423
|
-
"token_consumption": 104,
|
|
424
|
-
"mutation_score": 100.0,
|
|
425
|
-
"zero_side_effect": true,
|
|
426
|
-
"resolution_notes": "Replaced brittle timing with dynamic smart barrier: CDP_RESPONSE_BARRIER"
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
"id": "QA-CHALLENGE-14",
|
|
430
|
-
"name": "Async Chart SVG Animation Race Condition",
|
|
431
|
-
"category": "timing_and_race",
|
|
432
|
-
"difficulty": "MEDIUM",
|
|
433
|
-
"status": "PASSED",
|
|
434
|
-
"resolved": true,
|
|
435
|
-
"safety_cleared": true,
|
|
436
|
-
"time_to_heal_sec": 0.0,
|
|
437
|
-
"max_time_allowed_sec": 2.0,
|
|
438
|
-
"time_compliant": true,
|
|
439
|
-
"token_consumption": 82,
|
|
440
|
-
"mutation_score": 100.0,
|
|
441
|
-
"zero_side_effect": true,
|
|
442
|
-
"resolution_notes": "Replaced brittle timing with dynamic smart barrier: SVG_VISIBILITY_SMART_WAIT"
|
|
443
|
-
},
|
|
444
|
-
{
|
|
445
|
-
"id": "QA-CHALLENGE-15",
|
|
446
|
-
"name": "Infinite Scroll Lazy Loaded Chunk Detach",
|
|
447
|
-
"category": "timing_and_race",
|
|
448
|
-
"difficulty": "MEDIUM",
|
|
449
|
-
"status": "PASSED",
|
|
450
|
-
"resolved": true,
|
|
451
|
-
"safety_cleared": true,
|
|
452
|
-
"time_to_heal_sec": 0.0,
|
|
453
|
-
"max_time_allowed_sec": 2.0,
|
|
454
|
-
"time_compliant": true,
|
|
455
|
-
"token_consumption": 82,
|
|
456
|
-
"mutation_score": 100.0,
|
|
457
|
-
"zero_side_effect": true,
|
|
458
|
-
"resolution_notes": "Replaced brittle timing with dynamic smart barrier: VIRTUAL_SCROLL_INTO_VIEW"
|
|
459
|
-
}
|
|
460
|
-
]
|
|
461
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# 🌲 Cypress JavaScript Reference Suite (Clean POM & Synapse Guardrails)
|
|
2
|
-
|
|
3
|
-
This directory provides a production-grade **Cypress (JavaScript)** reference suite demonstrating the Clean Page Object Model (POM) and Flat Architecture standards enforced by **Synapse Hub**.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 🌟 End-to-End Testing Lifecycle (Story ➔ Test Cases ➔ Script ➔ Report)
|
|
8
|
-
|
|
9
|
-
```mermaid
|
|
10
|
-
flowchart LR
|
|
11
|
-
Story["1. User Story<br/>(E-Commerce Checkout)"] --> Matrix["2. Test Case Matrix<br/>(Equivalence & Boundary)"]
|
|
12
|
-
Matrix --> POM["3. Clean POM Pages<br/>(CartPage, CheckoutPage)"]
|
|
13
|
-
POM --> Spec["4. Cypress Spec<br/>(checkout.cy.js)"]
|
|
14
|
-
Spec --> Report["5. JUnit / Mochawesome<br/>(DoD Verified)"]
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
### 1. User Story & Acceptance Criteria
|
|
18
|
-
- **Story:** *As a customer, I want to review my shopping cart and apply a valid discount coupon so that my total price updates before placing an order.*
|
|
19
|
-
- **Acceptance Criteria (AC):**
|
|
20
|
-
- AC1: Cart must show items, quantities, and price breakdown.
|
|
21
|
-
- AC2: Valid coupon code (e.g. `SAVE20`) deducts 20% from subtotal.
|
|
22
|
-
- AC3: Invalid coupon code displays red error toast: *"Mã giảm giá không hợp lệ"*.
|
|
23
|
-
- AC4: Empty cart checkout button must remain disabled.
|
|
24
|
-
|
|
25
|
-
### 2. Key Architectural Patterns Enforced
|
|
26
|
-
- **Zero Raw Selectors in Specs:** `cypress/e2e/checkout.cy.js` calls only semantic methods on `CartPage` and `CheckoutPage`.
|
|
27
|
-
- **Resilient ARIA Locators:** Page objects use `cy.findByRole`, `cy.findByLabel`, or scoped semantic CSS.
|
|
28
|
-
- **Dynamic Idempotency:** Uses `BasePage.uniqueCode('ORDER_')` for order identifiers.
|
|
29
|
-
- **Smart Synchronization:** Replaces static `cy.wait(5000)` with network route assertions and spinner detachment.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## 🚀 How to Run & Verify
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
# 1. Run Synapse AST Test Linter (Zero-token verification)
|
|
37
|
-
npx synapse-hub lint ./cypress/e2e
|
|
38
|
-
|
|
39
|
-
# 2. Verify 9 Quality Gates DoD
|
|
40
|
-
npx synapse-hub verify
|
|
41
|
-
|
|
42
|
-
# 3. Run Headless Cypress
|
|
43
|
-
npm install
|
|
44
|
-
npx cypress run --spec "cypress/e2e/checkout.cy.js"
|
|
45
|
-
```
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const cartPage = require('../pages/CartPage');
|
|
2
|
-
|
|
3
|
-
describe('E-Commerce Cart & Discount Coupon Validation', () => {
|
|
4
|
-
beforeEach(() => {
|
|
5
|
-
cartPage.openCart();
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
it('TC-ECOM-01: Successfully apply 20% discount coupon to cart', () => {
|
|
9
|
-
cartPage.applyCoupon('SAVE20');
|
|
10
|
-
cartPage.verifyDiscountApplied('-20%');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('TC-ECOM-02: Display error when applying invalid or expired coupon', () => {
|
|
14
|
-
cartPage.applyCoupon('EXPIRED_COUPON_999');
|
|
15
|
-
cartPage.verifyCouponError('Mã giảm giá không hợp lệ');
|
|
16
|
-
});
|
|
17
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BasePage — Universal Cypress Base Page Object
|
|
3
|
-
* Enforces dynamic idempotency, smart waiting, and common interactions.
|
|
4
|
-
*/
|
|
5
|
-
class BasePage {
|
|
6
|
-
/**
|
|
7
|
-
* Generates dynamic unique identifier for test data isolation.
|
|
8
|
-
*/
|
|
9
|
-
static uniqueCode(prefix = 'AUTO_') {
|
|
10
|
-
return `${prefix}${Date.now()}_${Math.floor(Math.random() * 1000)}`;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Waits for loading spinner/overlay detachment.
|
|
15
|
-
*/
|
|
16
|
-
waitForSpinnerDetached() {
|
|
17
|
-
cy.get('.spinner-overlay, .loading-backdrop', { timeout: 10000 }).should('not.exist');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Navigates to a relative path.
|
|
22
|
-
*/
|
|
23
|
-
navigate(path = '/') {
|
|
24
|
-
cy.visit(path);
|
|
25
|
-
this.waitForSpinnerDetached();
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = BasePage;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const BasePage = require('./BasePage');
|
|
2
|
-
|
|
3
|
-
class CartPage extends BasePage {
|
|
4
|
-
// Encapsulated locators
|
|
5
|
-
get couponInput() {
|
|
6
|
-
return cy.get('input[name="couponCode"]');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
get applyCouponButton() {
|
|
10
|
-
return cy.contains('button', 'Áp dụng');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
get discountBadge() {
|
|
14
|
-
return cy.get('[data-testid="discount-amount"]');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
get errorMessageToast() {
|
|
18
|
-
return cy.get('.toast-error, .notification-error');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
get checkoutButton() {
|
|
22
|
-
return cy.contains('button', 'Tiến hành đặt hàng');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Business Action Methods
|
|
26
|
-
openCart() {
|
|
27
|
-
this.navigate('/cart');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
applyCoupon(couponCode) {
|
|
31
|
-
this.couponInput.clear().type(couponCode);
|
|
32
|
-
this.applyCouponButton.click();
|
|
33
|
-
this.waitForSpinnerDetached();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
proceedToCheckout() {
|
|
37
|
-
this.checkoutButton.click();
|
|
38
|
-
this.waitForSpinnerDetached();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Verification Methods
|
|
42
|
-
verifyDiscountApplied(expectedDiscount) {
|
|
43
|
-
this.discountBadge.should('be.visible').and('contain.text', expectedDiscount);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
verifyCouponError(expectedError) {
|
|
47
|
-
this.errorMessageToast.should('be.visible').and('contain.text', expectedError);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = new CartPage();
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const { defineConfig } = require('cypress');
|
|
2
|
-
|
|
3
|
-
module.exports = defineConfig({
|
|
4
|
-
e2e: {
|
|
5
|
-
baseUrl: 'https://staging-shop.example.com',
|
|
6
|
-
specPattern: 'cypress/e2e/**/*.cy.js',
|
|
7
|
-
supportFile: false,
|
|
8
|
-
viewportWidth: 1920,
|
|
9
|
-
viewportHeight: 1080,
|
|
10
|
-
video: false,
|
|
11
|
-
screenshotOnRunFailure: true,
|
|
12
|
-
},
|
|
13
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "cypress-ecommerce-suite",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Production Cypress Clean POM Reference Suite for Synapse-Hub",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"test": "cypress run",
|
|
7
|
-
"test:open": "cypress open",
|
|
8
|
-
"lint:synapse": "npx synapse-hub lint ./cypress/e2e"
|
|
9
|
-
},
|
|
10
|
-
"devDependencies": {
|
|
11
|
-
"cypress": "^13.6.0"
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
package pages
|
|
2
|
-
|
|
3
|
-
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
|
|
4
|
-
import com.kms.katalon.core.model.FailureHandling
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* BasePage encapsulating reusable Clean POM operations and smart synchronization.
|
|
8
|
-
*/
|
|
9
|
-
public class BasePage {
|
|
10
|
-
|
|
11
|
-
public static String uniqueCode(String prefix = "AUTO_") {
|
|
12
|
-
return prefix + System.currentTimeMillis()
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public void waitForReady(int timeoutSec = 10) {
|
|
16
|
-
// Smart wait: wait for page document state without static Thread.sleep
|
|
17
|
-
WebUI.waitForPageLoad(timeoutSec, FailureHandling.OPTIONAL)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
package pages
|
|
2
|
-
|
|
3
|
-
import com.kms.katalon.core.testobject.TestObject
|
|
4
|
-
import com.kms.katalon.core.testobject.ConditionType
|
|
5
|
-
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* DashboardPage displaying business KPIs and metrics.
|
|
9
|
-
*/
|
|
10
|
-
public class DashboardPage extends BasePage {
|
|
11
|
-
|
|
12
|
-
private TestObject headerDashboard = makeTestObject("//h1[contains(text(), 'Dashboard')]")
|
|
13
|
-
private TestObject cardActiveUsers = makeTestObject("//div[@data-testid='card-active-users']")
|
|
14
|
-
|
|
15
|
-
private static TestObject makeTestObject(String xpath) {
|
|
16
|
-
TestObject to = new TestObject()
|
|
17
|
-
to.addProperty("xpath", ConditionType.EQUALS, xpath)
|
|
18
|
-
return to
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public boolean isDashboardLoaded() {
|
|
22
|
-
waitForReady()
|
|
23
|
-
return WebUI.verifyElementPresent(headerDashboard, 10)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public String getActiveUsersCount() {
|
|
27
|
-
return WebUI.getText(cardActiveUsers)
|
|
28
|
-
}
|
|
29
|
-
}
|