synapse-hub 0.0.0-stage β 2.0.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/failure-analyst.md +47 -0
- package/.agents/agents/live-explorer.md +41 -0
- package/.agents/agents/orchestrator.md +43 -0
- package/.agents/agents/pom-crafter.md +81 -0
- package/.agents/agents/quality-gatekeeper.md +47 -0
- package/.agents/agents/test-runner.md +43 -0
- package/.agents/rules/context-engineering.md +88 -0
- package/.agents/rules/git-push-protocol.md +101 -0
- package/.agents/rules/graphify.md +14 -0
- package/.agents/rules/lessons-learned.md +23 -0
- package/.agents/rules/mandatory-playwright-live.md +29 -0
- package/.agents/rules/skill-precedence.md +42 -0
- package/.agents/rules/systematic-debugging.md +99 -0
- package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +239 -0
- package/.agents/skills/antigravity-test-sentinel/SKILL.md +64 -0
- package/.agents/skills/api-network-mocking-interception/SKILL.md +142 -0
- package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +70 -0
- package/.agents/skills/architecture-decision-records/SKILL.md +441 -0
- package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +72 -0
- package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +214 -0
- package/.agents/skills/bats-testing-patterns/SKILL.md +100 -0
- package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -0
- package/.agents/skills/clean-pom-crafter/SKILL.md +75 -0
- package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -0
- package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -0
- package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +64 -0
- package/.agents/skills/code-refactoring-refactor-clean/resources/implementation-playbook.md +879 -0
- package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +123 -0
- package/.agents/skills/e2e-testing-patterns/SKILL.md +103 -0
- package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -0
- package/.agents/skills/failure-autopsy-and-healing/SKILL.md +54 -0
- package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -0
- package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -0
- package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -0
- package/.agents/skills/file-and-report-validator/SKILL.md +90 -0
- package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +114 -0
- package/.agents/skills/github-actions-templates/SKILL.md +334 -0
- package/.agents/skills/graphify-impact-analyzer/SKILL.md +78 -0
- package/.agents/skills/katalon-cli-runner/SKILL.md +76 -0
- package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -0
- package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -0
- package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -0
- package/.agents/skills/lam-script-generator/SKILL.md +83 -0
- package/.agents/skills/playwright-live-interaction/SKILL.md +86 -0
- package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -0
- package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -0
- package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -0
- package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -0
- package/.agents/skills/security-dast-sentinel/SKILL.md +176 -0
- package/.agents/skills/self-healing-locator/SKILL.md +73 -0
- package/.agents/skills/synthetic-test-data-generator/SKILL.md +135 -0
- package/.agents/skills/token-and-context-optimizer/SKILL.md +70 -0
- package/.agents/skills/visual-regression-sentinel/SKILL.md +62 -0
- package/.agents/skills/wcag-audit-patterns/SKILL.md +77 -0
- package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -0
- package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +65 -0
- package/.agents/templates/completion_report.md +57 -0
- package/.agents/workflows/graphify.md +10 -0
- package/.agents/workflows/test-impact.md +15 -0
- package/.agents/workflows/verify.md +34 -0
- package/AGENTS.md +115 -0
- package/README.md +394 -2
- package/bin/agent-qa.js +90 -0
- package/package.json +59 -4
- package/skills_manifest.json +262 -0
- package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +97 -0
- package/templates/PROJECT_STATE_TEMPLATE.md +20 -0
- package/tools/TOOLS_RUNBOOK.md +298 -0
- package/tools/agent_canary.py +501 -0
- package/tools/analyze_project.py +242 -0
- package/tools/api_drift_sentinel.py +208 -0
- package/tools/auto_heal.py +189 -0
- package/tools/browser_helpers.js +593 -0
- package/tools/bug_reporter.py +224 -0
- package/tools/cache_resolver.py +846 -0
- package/tools/check_env.py +288 -0
- package/tools/ci_healing_hook.py +128 -0
- package/tools/cli.py +402 -0
- package/tools/config_decoupler.py +253 -0
- package/tools/db_sandbox.py +341 -0
- package/tools/distill_log.py +152 -0
- package/tools/e2e_flow_prober.ps1 +65 -0
- package/tools/flaky_detector.py +222 -0
- package/tools/fuzz_payloads.py +197 -0
- package/tools/generate_allure_report.ps1 +30 -0
- package/tools/generate_api_matrix.py +193 -0
- package/tools/generate_tc_from_story.py +182 -0
- package/tools/generate_test_reports.ps1 +135 -0
- package/tools/git_pre_commit_guard.py +203 -0
- package/tools/heal_pr_automator.py +195 -0
- package/tools/heuristic_locator_matcher.py +202 -0
- package/tools/infer_linter.py +238 -0
- package/tools/lint_ai_docs.py +254 -0
- package/tools/lookup_tc.py +165 -0
- package/tools/lookup_xpath.py +167 -0
- package/tools/matrix_sharder.py +146 -0
- package/tools/mcp_excel_server.ps1 +251 -0
- package/tools/mcp_server.py +490 -0
- package/tools/micro_mutator.py +309 -0
- package/tools/network_mock_shield.py +170 -0
- package/tools/omni_emitter.py +500 -0
- package/tools/omni_parser.py +435 -0
- package/tools/omni_transpiler.py +168 -0
- package/tools/pinpoint_failure.py +88 -0
- package/tools/project_state.py +169 -0
- package/tools/repo_map.json +12 -0
- package/tools/repo_map.py +277 -0
- package/tools/repo_map.txt +7 -0
- package/tools/safety_firewall.py +245 -0
- package/tools/setup_project.py +265 -0
- package/tools/smart_visual_masker.py +134 -0
- package/tools/test_impact.py +321 -0
- package/tools/tms_sync_bridge.py +342 -0
- package/tools/trace_data_flow.py +240 -0
- package/tools/traffic_session_converter.py +256 -0
- package/tools/universal_ir.py +480 -0
- package/tools/validate_groovy_scripts.py +173 -0
- package/tools/verify_all.py +496 -0
- package/tools/verify_all.sh +4 -0
- package/tools/visual_diff_canvas.py +142 -0
- package/tools/visual_som_helper.py +418 -0
- package/tools/wcag_audit.py +214 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# Enterprise Playwright Code Recipes (20 Recipes)
|
|
2
|
+
|
|
3
|
+
All code recipes below execute directly via `browser_run_code_unsafe` with parameter `code: "async (page) => { ... }"`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Recipe 1: Authentication & Session Verification
|
|
8
|
+
```javascript
|
|
9
|
+
async (page) => {
|
|
10
|
+
if (page.url().includes('/auth') || page.url().includes('/login')) {
|
|
11
|
+
const user = process.env.APP_USERNAME || 'your_username';
|
|
12
|
+
const pass = process.env.APP_PASSWORD || 'your_password';
|
|
13
|
+
await page.locator('input[formcontrolname="username"]').fill(user);
|
|
14
|
+
await page.locator('input[formcontrolname="password"]').fill(pass);
|
|
15
|
+
await page.locator('button[type="submit"]').click();
|
|
16
|
+
await page.waitForNavigation({ waitUntil: 'networkidle' });
|
|
17
|
+
}
|
|
18
|
+
return { currentUrl: page.url(), title: await page.title() };
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Recipe 2: Search, Filter & PrimeNG Table Verification (`p-table`)
|
|
23
|
+
```javascript
|
|
24
|
+
async (page) => {
|
|
25
|
+
const searchInput = page.locator('input.p-inputtext[placeholder*="TΓ¬m kiαΊΏm"], input[placeholder*="tΓͺn"], input[placeholder*="mΓ£"]').first();
|
|
26
|
+
await searchInput.fill('HΓ Nα»i');
|
|
27
|
+
await searchInput.press('Enter');
|
|
28
|
+
await page.waitForTimeout(1000);
|
|
29
|
+
|
|
30
|
+
const rowCount = await page.locator('p-table table tbody tr').count();
|
|
31
|
+
const firstRowText = rowCount > 0 ? await page.locator('p-table table tbody tr').first().innerText() : '';
|
|
32
|
+
return { rowCount, firstRowText };
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Recipe 3: PrimeNG Dropdown & MultiSelect (`p-dropdown`, `p-multiselect`)
|
|
37
|
+
```javascript
|
|
38
|
+
async (page) => {
|
|
39
|
+
const dropdown = page.locator('p-dropdown[formcontrolname="status"], p-dropdown[name="status"]').first();
|
|
40
|
+
await dropdown.click();
|
|
41
|
+
await page.waitForTimeout(400);
|
|
42
|
+
|
|
43
|
+
const option = page.locator('.p-dropdown-panel .p-dropdown-item').filter({ hasText: 'Δang hoαΊ‘t Δα»ng' }).first();
|
|
44
|
+
await option.click();
|
|
45
|
+
await page.waitForTimeout(300);
|
|
46
|
+
|
|
47
|
+
return { selectedLabel: await dropdown.locator('.p-dropdown-label').innerText() };
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Recipe 4: Modal Dialogs & Popup Forms (`p-dialog`, `.card.popup-data`)
|
|
52
|
+
```javascript
|
|
53
|
+
async (page) => {
|
|
54
|
+
await page.evaluate(() => {
|
|
55
|
+
function setAngularInput(selector, val) {
|
|
56
|
+
const el = document.querySelector(selector);
|
|
57
|
+
if (el) {
|
|
58
|
+
el.value = val;
|
|
59
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
60
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const testCode = 'CCN_' + Math.floor(Date.now() / 1000);
|
|
64
|
+
setAngularInput('input[formcontrolname="code"]', testCode);
|
|
65
|
+
setAngularInput('input[formcontrolname="name"]', 'Cα»₯m CΓ΄ng Nghiα»p ' + testCode);
|
|
66
|
+
setAngularInput('textarea[formcontrolname="address"]', 'Δα»a chα» kiα»m thα» tα»± Δα»ng');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const saveBtn = page.locator('button:has-text("LΖ°u"), button:has-text("XΓ‘c nhαΊn"), button[title="XΓ‘c nhαΊn"]').first();
|
|
70
|
+
const isEnabled = !(await saveBtn.isDisabled());
|
|
71
|
+
if (isEnabled) {
|
|
72
|
+
await saveBtn.click();
|
|
73
|
+
await page.waitForTimeout(1500);
|
|
74
|
+
}
|
|
75
|
+
return { isSaveButtonEnabled: isEnabled };
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Recipe 5: Confirm Dialog & Deletion (`p-confirmdialog`)
|
|
80
|
+
```javascript
|
|
81
|
+
async (page) => {
|
|
82
|
+
await page.locator('p-table tbody tr button.p-button-danger, p-table tbody tr button[icon*="trash"]').first().click();
|
|
83
|
+
await page.waitForTimeout(500);
|
|
84
|
+
|
|
85
|
+
const confirmBtn = page.locator('.p-confirm-dialog-accept, button:has-text("Δα»ng Γ½"), button:has-text("XΓ³a")').first();
|
|
86
|
+
await confirmBtn.click();
|
|
87
|
+
await page.waitForTimeout(1000);
|
|
88
|
+
|
|
89
|
+
const toastText = await page.locator('.p-toast-message-content').innerText().catch(() => 'No toast');
|
|
90
|
+
return { toastText };
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Recipe 6: File Upload & Excel Import (`input[type="file"]`)
|
|
95
|
+
```javascript
|
|
96
|
+
async (page, customPath = 'Data Files/sample_import.xlsx') => {
|
|
97
|
+
const fs = require('fs');
|
|
98
|
+
const filePath = fs.existsSync(customPath) ? customPath : 'Data Files/sample_import.xlsx';
|
|
99
|
+
await page.locator('input[type="file"]').setInputFiles(filePath);
|
|
100
|
+
await page.waitForTimeout(1000);
|
|
101
|
+
|
|
102
|
+
await page.locator('button:has-text("TαΊ£i lΓͺn"), button:has-text("XΓ‘c nhαΊn")').first().click();
|
|
103
|
+
await page.waitForTimeout(2000);
|
|
104
|
+
return { status: "Uploaded", fileUsed: filePath };
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Recipe 7: OpenLayers GIS Canvas Interaction (`div.ol-viewport`)
|
|
109
|
+
```javascript
|
|
110
|
+
async (page) => {
|
|
111
|
+
const canvas = page.locator('div.ol-viewport canvas').first();
|
|
112
|
+
const box = await canvas.boundingBox();
|
|
113
|
+
const cx = box.x + box.width / 2;
|
|
114
|
+
const cy = box.y + box.height / 2;
|
|
115
|
+
|
|
116
|
+
// Click polygon at non-overlapping offset (-180, -80)
|
|
117
|
+
await page.mouse.click(cx - 180, cy - 80);
|
|
118
|
+
await page.waitForTimeout(400);
|
|
119
|
+
await page.mouse.click(cx - 150, cy - 80);
|
|
120
|
+
await page.waitForTimeout(400);
|
|
121
|
+
await page.mouse.click(cx - 150, cy - 50);
|
|
122
|
+
await page.waitForTimeout(400);
|
|
123
|
+
await page.mouse.dblclick(cx - 180, cy - 80);
|
|
124
|
+
await page.waitForTimeout(1000);
|
|
125
|
+
|
|
126
|
+
const result = await page.evaluate(() => {
|
|
127
|
+
const error = document.querySelector('.text-danger, .invalid-feedback, editor-widget');
|
|
128
|
+
return {
|
|
129
|
+
hasError: !!error,
|
|
130
|
+
errorText: error ? error.innerText : ''
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Recipe 8: Advanced OpenLayers Map Inspection & VN2000 Coordinates
|
|
138
|
+
```javascript
|
|
139
|
+
async (page) => {
|
|
140
|
+
return await page.evaluate(() => {
|
|
141
|
+
const viewport = document.querySelector('div.ol-viewport');
|
|
142
|
+
if (!viewport) return { error: "div.ol-viewport not found" };
|
|
143
|
+
|
|
144
|
+
const map = viewport['__ol_map__'] || (window.map instanceof Object ? window.map : null);
|
|
145
|
+
let mapInfo = {};
|
|
146
|
+
if (map && map.getView) {
|
|
147
|
+
const view = map.getView();
|
|
148
|
+
mapInfo = {
|
|
149
|
+
zoom: view.getZoom(),
|
|
150
|
+
center: view.getCenter(),
|
|
151
|
+
projection: view.getProjection() ? view.getProjection().getCode() : 'Unknown'
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const toolbarButtons = Array.from(document.querySelectorAll('.ol-control button, .map-toolbar button, .gis-tools button'))
|
|
155
|
+
.map(b => ({ title: b.getAttribute('title') || b.innerText.trim(), className: b.className }));
|
|
156
|
+
return { mapInfo, toolbarButtons };
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Recipe 9: Two-Phase Search Workflow
|
|
162
|
+
```javascript
|
|
163
|
+
async (page) => {
|
|
164
|
+
const searchToggleBtn = page.locator('button.p-button-icon-only[icon*="search"], button[title*="Tìm kiếm"], button:has-text("Tìm kiếm")').first();
|
|
165
|
+
const searchInput = page.locator('input.p-inputtext[placeholder*="TΓ¬m kiαΊΏm"], input[placeholder*="tΓͺn"], input[placeholder*="mΓ£"]').first();
|
|
166
|
+
|
|
167
|
+
if (!(await searchInput.isVisible())) {
|
|
168
|
+
await searchToggleBtn.click();
|
|
169
|
+
await page.waitForTimeout(300);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const keyword = 'CCN_TEST_2026';
|
|
173
|
+
await searchInput.fill(keyword);
|
|
174
|
+
await searchInput.press('Enter');
|
|
175
|
+
await page.waitForTimeout(1000);
|
|
176
|
+
|
|
177
|
+
const rowCount = await page.locator('p-table table tbody tr').count();
|
|
178
|
+
const matched = rowCount > 0 && (await page.locator('p-table table tbody tr').first().innerText()).includes(keyword);
|
|
179
|
+
return { rowCount, matched };
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Recipe 10: Intercepting PrimeNG Toasts (`p-toast`)
|
|
184
|
+
```javascript
|
|
185
|
+
async (page) => {
|
|
186
|
+
const toastSelector = '.p-toast-message, .p-toast-message-content';
|
|
187
|
+
try {
|
|
188
|
+
const toast = await page.waitForSelector(toastSelector, { timeout: 4000 });
|
|
189
|
+
const text = await toast.innerText();
|
|
190
|
+
const isSuccess = (await toast.getAttribute('class') || '').includes('p-toast-message-success');
|
|
191
|
+
return { success: true, text, isSuccess };
|
|
192
|
+
} catch (e) {
|
|
193
|
+
return { success: false, message: 'Toast did not appear or expired too quickly' };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Recipe 11: Record Deletion & Confirmation Dialog (`p-confirmdialog`)
|
|
199
|
+
```javascript
|
|
200
|
+
async (page, targetKeyword) => {
|
|
201
|
+
const searchInput = page.locator('input.p-inputtext[placeholder*="TΓ¬m kiαΊΏm"], input[placeholder*="tΓͺn"], input[placeholder*="mΓ£"]').first();
|
|
202
|
+
await searchInput.fill(targetKeyword);
|
|
203
|
+
await searchInput.press('Enter');
|
|
204
|
+
await page.waitForTimeout(1000);
|
|
205
|
+
|
|
206
|
+
const deleteBtn = page.locator('p-table tbody tr:first-child button.p-button-danger, p-table tbody tr:first-child button[icon*="trash"]').first();
|
|
207
|
+
await deleteBtn.click();
|
|
208
|
+
await page.waitForTimeout(500);
|
|
209
|
+
|
|
210
|
+
const confirmBtn = page.locator('.p-confirm-dialog-accept, .p-dialog-footer button:has-text("XΓ³a"), .p-dialog-footer button:has-text("Δα»ng Γ½"), button.p-button-danger:has-text("XΓ³a")').first();
|
|
211
|
+
await confirmBtn.waitFor({ state: 'visible', timeout: 3000 });
|
|
212
|
+
await confirmBtn.click();
|
|
213
|
+
await page.waitForTimeout(1500);
|
|
214
|
+
|
|
215
|
+
await searchInput.fill(targetKeyword);
|
|
216
|
+
await searchInput.press('Enter');
|
|
217
|
+
await page.waitForTimeout(1000);
|
|
218
|
+
const remainingRows = await page.locator('p-table table tbody tr').count();
|
|
219
|
+
const firstRowText = remainingRows > 0 ? await page.locator('p-table table tbody tr').first().innerText() : '';
|
|
220
|
+
const isDeleted = remainingRows === 0 || !firstRowText.includes(targetKeyword) || firstRowText.includes('KhΓ΄ng cΓ³ dα»― liα»u');
|
|
221
|
+
|
|
222
|
+
return { isDeleted, remainingRows, firstRowText };
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Recipe 12: PrimeNG Calendar Date Input (`p-calendar`)
|
|
227
|
+
```javascript
|
|
228
|
+
async (page) => {
|
|
229
|
+
const dateValue = '25/10/2026';
|
|
230
|
+
await page.evaluate(({ selector, val }) => {
|
|
231
|
+
const calInput = document.querySelector(selector);
|
|
232
|
+
if (calInput) {
|
|
233
|
+
calInput.value = val;
|
|
234
|
+
calInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
235
|
+
calInput.dispatchEvent(new Event('change', { bubbles: true }));
|
|
236
|
+
calInput.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
237
|
+
}
|
|
238
|
+
}, { selector: 'p-calendar input, input[formcontrolname*="date"], input[formcontrolname*="Date"]', val: dateValue });
|
|
239
|
+
|
|
240
|
+
await page.waitForTimeout(300);
|
|
241
|
+
return { status: 'Date filled', value: dateValue };
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Recipe 13: PrimeNG MultiSelect Dropdown (`p-multiselect`)
|
|
246
|
+
```javascript
|
|
247
|
+
async (page, optionsToSelect = ['CΓ΄ng nghiα»p chαΊΏ biαΊΏn', 'Δiα»n lα»±c']) => {
|
|
248
|
+
const multiSelect = page.locator('p-multiselect').first();
|
|
249
|
+
await multiSelect.click();
|
|
250
|
+
await page.waitForSelector('.p-multiselect-panel', { state: 'visible', timeout: 3000 });
|
|
251
|
+
await page.waitForTimeout(300);
|
|
252
|
+
|
|
253
|
+
for (const optText of optionsToSelect) {
|
|
254
|
+
const item = page.locator('.p-multiselect-item').filter({ hasText: optText }).first();
|
|
255
|
+
if (await item.isVisible()) {
|
|
256
|
+
await item.click();
|
|
257
|
+
await page.waitForTimeout(200);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const closeBtn = page.locator('.p-multiselect-close').first();
|
|
262
|
+
if (await closeBtn.isVisible()) {
|
|
263
|
+
await closeBtn.click();
|
|
264
|
+
} else {
|
|
265
|
+
await page.keyboard.press('Escape');
|
|
266
|
+
}
|
|
267
|
+
await page.waitForTimeout(300);
|
|
268
|
+
return { selected: optionsToSelect };
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Recipe 14: Handling Stale Elements & Angular DOM Re-render Retries
|
|
273
|
+
```javascript
|
|
274
|
+
async (page, actionFn) => {
|
|
275
|
+
const maxRetries = 3;
|
|
276
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
277
|
+
try {
|
|
278
|
+
return await actionFn(page);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
if (err.message.includes('Element is not attached to the DOM') || err.message.includes('stale') || attempt === maxRetries) {
|
|
281
|
+
if (attempt === maxRetries) throw err;
|
|
282
|
+
await page.waitForTimeout(500 * attempt);
|
|
283
|
+
} else {
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
## Recipe 15: Instant Angular Form Audit (Diagnose Disabled Save Button)
|
|
293
|
+
```javascript
|
|
294
|
+
async (page) => {
|
|
295
|
+
return await page.evaluate(() => {
|
|
296
|
+
const invalidInputs = Array.from(document.querySelectorAll('form .ng-invalid, input.ng-invalid, p-dropdown.ng-invalid, textarea.ng-invalid'));
|
|
297
|
+
return invalidInputs.map(el => ({
|
|
298
|
+
tag: el.tagName.toLowerCase(),
|
|
299
|
+
control: el.getAttribute('formcontrolname') || el.getAttribute('name') || el.id || 'unnamed',
|
|
300
|
+
placeholder: el.getAttribute('placeholder') || '',
|
|
301
|
+
currentValue: el.value || el.innerText.trim()
|
|
302
|
+
}));
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Recipe 16: Smart Action Batching (Atomic Execution with Pause-on-Failure)
|
|
308
|
+
```javascript
|
|
309
|
+
async (page) => {
|
|
310
|
+
return await page.evaluate(() => {
|
|
311
|
+
const steps = [
|
|
312
|
+
{ name: 'fill_code', fn: () => { const el = document.querySelector("input[formcontrolname='code']"); el.value = 'CCN_TEST'; el.dispatchEvent(new Event('input', {bubbles: true})); } },
|
|
313
|
+
{ name: 'fill_name', fn: () => { const el = document.querySelector("input[formcontrolname='name']"); el.value = 'Cα»₯m CN Test'; el.dispatchEvent(new Event('input', {bubbles: true})); } },
|
|
314
|
+
{ name: 'submit', fn: () => { document.querySelector("button[type='submit']").click(); } }
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
for (let i = 0; i < steps.length; i++) {
|
|
318
|
+
try {
|
|
319
|
+
steps[i].fn();
|
|
320
|
+
} catch (err) {
|
|
321
|
+
return { status: 'FAILED', failedStep: i + 1, name: steps[i].name, error: err.message, url: location.href };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return { status: 'SUCCESS', totalSteps: steps.length };
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Recipe 17: Runtime Helper Injection (`window.__qa`)
|
|
330
|
+
```javascript
|
|
331
|
+
async (page) => {
|
|
332
|
+
// Injects the centralized browser helper tools/browser_helpers.js
|
|
333
|
+
const fs = require('fs');
|
|
334
|
+
const helperCode = fs.readFileSync('tools/browser_helpers.js', 'utf8');
|
|
335
|
+
await page.evaluate(helperCode);
|
|
336
|
+
return await page.evaluate(() => ({
|
|
337
|
+
version: window.__qa.__version,
|
|
338
|
+
auditResults: window.__qa.audit()
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Recipe 18: Self-Healing Locator Fallback (`window.__qa.heal`)
|
|
344
|
+
```javascript
|
|
345
|
+
async (page, brokenXPath, expectedText) => {
|
|
346
|
+
return await page.evaluate(({ xpath, text }) => {
|
|
347
|
+
if (window.__qa) {
|
|
348
|
+
return window.__qa.heal(xpath, text);
|
|
349
|
+
}
|
|
350
|
+
// Fallback search
|
|
351
|
+
const buttons = Array.from(document.querySelectorAll('button')).filter(b => b.innerText.includes(text));
|
|
352
|
+
return {
|
|
353
|
+
candidates: buttons.map(b => `//button[contains(normalize-space(.), '${b.innerText.trim()}')]`)
|
|
354
|
+
};
|
|
355
|
+
}, { xpath: brokenXPath, text: expectedText });
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Recipe 19: Overlay-Proof Safe Click (`window.__qa.clickSafe`)
|
|
360
|
+
```javascript
|
|
361
|
+
async (page, selector) => {
|
|
362
|
+
return await page.evaluate((sel) => {
|
|
363
|
+
if (window.__qa) {
|
|
364
|
+
return window.__qa.clickSafe(sel);
|
|
365
|
+
}
|
|
366
|
+
const el = document.querySelector(sel);
|
|
367
|
+
if (!el) return false;
|
|
368
|
+
document.querySelectorAll('.p-component-overlay').forEach(o => o.style.pointerEvents = 'none');
|
|
369
|
+
el.click();
|
|
370
|
+
return true;
|
|
371
|
+
}, selector);
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Recipe 20: OpenLayers GIS Canvas Coordinate Inspector (`window.__qa.gisHUD`)
|
|
376
|
+
```javascript
|
|
377
|
+
async (page) => {
|
|
378
|
+
return await page.evaluate(() => {
|
|
379
|
+
if (window.__qa) {
|
|
380
|
+
return window.__qa.gisHUD();
|
|
381
|
+
}
|
|
382
|
+
const vp = document.querySelector('.ol-viewport');
|
|
383
|
+
if (!vp) return { status: 'NO_MAP' };
|
|
384
|
+
const r = vp.getBoundingClientRect();
|
|
385
|
+
return { width: r.width, height: r.height, safeCenter: { x: r.width / 2, y: r.height / 2 } };
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
```
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Enterprise Multi-Agent Swarm Orchestration Guide (Enterprise Automation)
|
|
2
|
+
|
|
3
|
+
Standard operating procedure, role taxonomy, and collaboration protocol for the **Enterprise Test Engineering Swarm** covering all 259 Test Cases.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Organizational Hierarchy & Role Taxonomy
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart TD
|
|
11
|
+
Boss["π PROJECT LEAD (Test Manager / QA Director)<br/>(Strategic direction & final sign-off)"] --> Head["π LEAD QA ORCHESTRATOR<br/>(Overall coordination & quality governance)"]
|
|
12
|
+
|
|
13
|
+
subgraph SpecializedSwarm["SPECIALIZED AUTOMATION SWARM"]
|
|
14
|
+
Scout["π LIVE INTERACTION EXPLORER<br/>(DOM/AOM live probing via Playwright MCP)"]
|
|
15
|
+
Crafter["βοΈ AUTOMATION CODE CRAFTER<br/>(Page Classes & Clean POM Groovy scripts)"]
|
|
16
|
+
Runner["β‘ TEST EXECUTION RUNNER<br/>(Headless Katalon CLI execution)"]
|
|
17
|
+
Judge["π‘οΈ QUALITY GATEKEEPER & ANALYST<br/>(100% UTF-8 No BOM, anti-bypass & RCA)"]
|
|
18
|
+
Scribe["π TECHNICAL DOCUMENTATION SPECIALIST<br/>(Business workflows & visual charting)"]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Head --> Scout & Crafter & Runner & Judge & Scribe
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 2. Enterprise Collaboration Protocol
|
|
27
|
+
|
|
28
|
+
1. **User Communication (Project Lead):**
|
|
29
|
+
* Demeanor: Results-oriented, data-driven, concise, transparent.
|
|
30
|
+
* Language with user: Vietnamese (clear engineering terms).
|
|
31
|
+
2. **Internal Agent-to-Agent Protocol:**
|
|
32
|
+
* **Lead QA Orchestrator:** Decomposes Acceptance Criteria, manages timeline, monitors SLAs.
|
|
33
|
+
* **Live Interaction Explorer:** Delivers validated JSON Data Contracts based on live DOM inspection.
|
|
34
|
+
* **Automation Code Crafter:** Implements Page Object Model code under test scrutiny from Runner and gatekeeping from Gatekeeper.
|
|
35
|
+
* **Quality Gatekeeper & Analyst:** Enforces quality standards independently; rejects fake passes; provides RCA evidence.
|
|
36
|
+
* **Technical Documentation Specialist:** Systematizes documentation, translates code into visual Mermaid workflows.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 3. Specialized Role Responsibilities
|
|
41
|
+
|
|
42
|
+
### π 1. Lead QA Orchestrator
|
|
43
|
+
* Receives objectives from Project Lead (e.g. *"Complete Industrial Clusters TC-66 to TC-80"*).
|
|
44
|
+
* Queries `tools/lookup_tc.py` to break down tasks into subagent batches.
|
|
45
|
+
* Invokes specialized subagents and synthesizes completion reports.
|
|
46
|
+
|
|
47
|
+
### π 2. Live Interaction Explorer
|
|
48
|
+
* **Tools:** Playwright MCP (`browser_run_code_unsafe`, `browser_navigate`).
|
|
49
|
+
* **Tasks:**
|
|
50
|
+
- Loads `.agents/session.json` (Zero-Second Auth).
|
|
51
|
+
- Inspects live routes: URL, Title, interactive buttons, inputs, dropdown overlays.
|
|
52
|
+
- Verifies 100% selector hit rate on live browser.
|
|
53
|
+
- Returns compact JSON Data Contract to Code Crafter.
|
|
54
|
+
|
|
55
|
+
### βοΈ 3. Automation Code Crafter
|
|
56
|
+
* **Tools:** File editing tools.
|
|
57
|
+
* **Tasks:**
|
|
58
|
+
- Consumes Data Contract from Explorer.
|
|
59
|
+
- Implements `public static` methods in `Keywords/pages/<PageClass>.groovy` inheriting `BasePage`.
|
|
60
|
+
- Generates corresponding test scripts (`Scripts/TC-xxx .../Script*.groovy`).
|
|
61
|
+
- Strict compliance with Flat Architecture and Clean POM (zero raw XPaths in test scripts).
|
|
62
|
+
|
|
63
|
+
### β‘ 4. Test Execution Runner
|
|
64
|
+
* **Tools:** Run command tools (`katalonc`, shell/PowerShell runners).
|
|
65
|
+
* **Tasks:**
|
|
66
|
+
- Executes targeted suites or batches in pure Headless Mode.
|
|
67
|
+
- Captures `Screenshots/FAILED_*.png` on failure; compiles Allure reports.
|
|
68
|
+
- Hands over results to Quality Gatekeeper.
|
|
69
|
+
|
|
70
|
+
### π‘οΈ 5. Quality Gatekeeper & Failure Analyst
|
|
71
|
+
* **Tools:** Static checkers, test assertion validators.
|
|
72
|
+
* **Tasks:**
|
|
73
|
+
- Enforces 100% UTF-8 No BOM (`0xEF, 0xBB, 0xBF`).
|
|
74
|
+
- Rejects fake passes (verifies real toast notifications and table data).
|
|
75
|
+
- Conducts visual RCA via `triage_failures.sh` and `Screenshots/FAILED_*.png`.
|
|
76
|
+
|
|
77
|
+
### π 6. Technical Documentation Specialist
|
|
78
|
+
* **Tasks:**
|
|
79
|
+
- Translates `.groovy` scripts into plain business requirements.
|
|
80
|
+
- Generates Mermaid sequence and flowchart diagrams.
|
|
81
|
+
- Documents technical rationale (why specific coordinates or synthetic events are required).
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 4. Launching Swarm in Antigravity
|
|
86
|
+
|
|
87
|
+
- **Method 1 (UI Slash Command):** Type `/teamwork-preview` to visualize parallel subagents.
|
|
88
|
+
- **Method 2 (Autonomous Prompt):** Prompt: *"Act as Lead QA Orchestrator, dispatch Swarm to complete TC-123 through TC-127."*
|
|
89
|
+
* Orchestrator invokes `Live Explorer` for DOM probing.
|
|
90
|
+
* Next invokes `Code Crafter` to write Groovy code.
|
|
91
|
+
* Finally invokes `Quality Gatekeeper` for verification.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 5. Multi-Agent Adversarial Debate
|
|
96
|
+
|
|
97
|
+
```mermaid
|
|
98
|
+
sequenceDiagram
|
|
99
|
+
participant Crafter as βοΈ Automation Code Crafter
|
|
100
|
+
participant Explorer as π Live Interaction Explorer
|
|
101
|
+
participant Gatekeeper as π‘οΈ Quality Gatekeeper
|
|
102
|
+
participant Judge as π Lead QA Orchestrator
|
|
103
|
+
|
|
104
|
+
Crafter->>Explorer: "Created Supermarket method drawing Polygon at (-180, -80)."
|
|
105
|
+
Explorer-->>Crafter: "CHALLENGE: Live DOM shows Supermarket is POINT, not POLYGON! Will fail like TC-135."
|
|
106
|
+
Crafter->>Crafter: Refactors to clickPointOnCanvas().
|
|
107
|
+
Crafter->>Gatekeeper: "Submitted updated Point implementation."
|
|
108
|
+
Gatekeeper-->>Crafter: "CHALLENGE: Only delay(3) without verifying real success Toast. Fake pass detected!"
|
|
109
|
+
Crafter->>Crafter: Adds verifyMiningSuccess('thΓ nh cΓ΄ng').
|
|
110
|
+
Crafter->>Gatekeeper: "Added real Toast assertion + verified UTF-8 No BOM."
|
|
111
|
+
Gatekeeper->>Judge: "REPORT: Code 100% compliant."
|
|
112
|
+
Judge->>Judge: Consensus reached -> Commit code!
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 6. Model Routing Strategy (Quota Optimization)
|
|
118
|
+
|
|
119
|
+
| Subagent Role | Recommended Model | Rationale & Tasks |
|
|
120
|
+
|---|:---:|---|
|
|
121
|
+
| **Quality Gatekeeper & Failure Analyst** | `flash` / `flash_lite` | Parses long KRE console logs, checks JUnit XML & BOM, ultra-fast (<0.5s), saves 70% quota. |
|
|
122
|
+
| **Test Execution Runner** | `flash` | Executes CLI runners, checks exit codes. |
|
|
123
|
+
| **Live Interaction Explorer** | `pro` / `inherit` | Complex DOM inspection, OpenLayers GIS navigation, AOM snapshots. |
|
|
124
|
+
| **Automation Code Crafter** | `pro` / `inherit` | Clean POM Groovy refactoring, Angular synthetic event handling. |
|
|
125
|
+
| **Lead QA Orchestrator** | `pro` / `inherit` | Acceptance Criteria decomposition and architectural decision-making. |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 7. Auto Cleanup Blacklist
|
|
130
|
+
|
|
131
|
+
All Subagents **MUST** clean up temporary files before returning control to Orchestrator:
|
|
132
|
+
- Dumps: `*_debug.txt`, `debug_output.txt`, `dom_dump.txt`, `html_dump.html`, `network_requests.txt`, `console_log.txt`
|
|
133
|
+
- Snapshots: `page_snapshot.md`, `snapshot_*.md`, `.agents/screenshots/gis_canvas_live.png`
|
|
134
|
+
- Scratch files: `scratch_*.*`, `*.tmp`, `*.temp`
|