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,522 @@
|
|
|
1
|
+
# E2E Testing Patterns Implementation Playbook
|
|
2
|
+
|
|
3
|
+
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
### 1. E2E Testing Fundamentals
|
|
8
|
+
|
|
9
|
+
**What to Test with E2E:**
|
|
10
|
+
- Critical user journeys (login, checkout, signup)
|
|
11
|
+
- Complex interactions (drag-and-drop, multi-step forms)
|
|
12
|
+
- Cross-browser compatibility
|
|
13
|
+
- Real API integration
|
|
14
|
+
- Authentication flows
|
|
15
|
+
|
|
16
|
+
**What NOT to Test with E2E:**
|
|
17
|
+
- Unit-level logic (use unit tests)
|
|
18
|
+
- API contracts (use integration tests)
|
|
19
|
+
- Edge cases (too slow)
|
|
20
|
+
- Internal implementation details
|
|
21
|
+
|
|
22
|
+
### 2. Test Philosophy
|
|
23
|
+
|
|
24
|
+
**The Testing Pyramid:**
|
|
25
|
+
```
|
|
26
|
+
/\
|
|
27
|
+
/E2E\ ← Few, focused on critical paths
|
|
28
|
+
/─────\
|
|
29
|
+
/Integr\ ← More, test component interactions
|
|
30
|
+
/────────\
|
|
31
|
+
/Unit Tests\ ← Many, fast, isolated
|
|
32
|
+
/────────────\
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Best Practices:**
|
|
36
|
+
- Test user behavior, not implementation
|
|
37
|
+
- Keep tests independent
|
|
38
|
+
- Make tests deterministic
|
|
39
|
+
- Optimize for speed
|
|
40
|
+
- Use data-testid, not CSS selectors
|
|
41
|
+
|
|
42
|
+
## Playwright Patterns
|
|
43
|
+
|
|
44
|
+
### Setup and Configuration
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// playwright.config.ts
|
|
48
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
49
|
+
|
|
50
|
+
export default defineConfig({
|
|
51
|
+
testDir: './e2e',
|
|
52
|
+
timeout: 30000,
|
|
53
|
+
expect: {
|
|
54
|
+
timeout: 5000,
|
|
55
|
+
},
|
|
56
|
+
fullyParallel: true,
|
|
57
|
+
forbidOnly: !!process.env.CI,
|
|
58
|
+
retries: process.env.CI ? 2 : 0,
|
|
59
|
+
workers: process.env.CI ? 1 : undefined,
|
|
60
|
+
reporter: [
|
|
61
|
+
['html'],
|
|
62
|
+
['junit', { outputFile: 'results.xml' }],
|
|
63
|
+
],
|
|
64
|
+
use: {
|
|
65
|
+
baseURL: 'http://localhost:3000',
|
|
66
|
+
trace: 'on-first-retry',
|
|
67
|
+
screenshot: 'only-on-failure',
|
|
68
|
+
video: 'retain-on-failure',
|
|
69
|
+
},
|
|
70
|
+
projects: [
|
|
71
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
72
|
+
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
73
|
+
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
|
74
|
+
{ name: 'mobile', use: { ...devices['iPhone 13'] } },
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Pattern 1: Page Object Model
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// pages/LoginPage.ts
|
|
83
|
+
import { Page, Locator } from '@playwright/test';
|
|
84
|
+
|
|
85
|
+
export class LoginPage {
|
|
86
|
+
readonly page: Page;
|
|
87
|
+
readonly emailInput: Locator;
|
|
88
|
+
readonly passwordInput: Locator;
|
|
89
|
+
readonly loginButton: Locator;
|
|
90
|
+
readonly errorMessage: Locator;
|
|
91
|
+
|
|
92
|
+
constructor(page: Page) {
|
|
93
|
+
this.page = page;
|
|
94
|
+
this.emailInput = page.getByLabel('Email');
|
|
95
|
+
this.passwordInput = page.getByLabel('Password');
|
|
96
|
+
this.loginButton = page.getByRole('button', { name: 'Login' });
|
|
97
|
+
this.errorMessage = page.getByRole('alert');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async goto() {
|
|
101
|
+
await this.page.goto('/login');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async login(email: string, password: string) {
|
|
105
|
+
await this.emailInput.fill(email);
|
|
106
|
+
await this.passwordInput.fill(password);
|
|
107
|
+
await this.loginButton.click();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getErrorMessage(): Promise<string> {
|
|
111
|
+
return await this.errorMessage.textContent() ?? '';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Test using Page Object
|
|
116
|
+
import { test, expect } from '@playwright/test';
|
|
117
|
+
import { LoginPage } from './pages/LoginPage';
|
|
118
|
+
|
|
119
|
+
test('successful login', async ({ page }) => {
|
|
120
|
+
const loginPage = new LoginPage(page);
|
|
121
|
+
await loginPage.goto();
|
|
122
|
+
await loginPage.login('user@example.com', process.env.TEST_PASSWORD || 'SecretAuth123!');
|
|
123
|
+
|
|
124
|
+
await expect(page).toHaveURL('/dashboard');
|
|
125
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' }))
|
|
126
|
+
.toBeVisible();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('failed login shows error', async ({ page }) => {
|
|
130
|
+
const loginPage = new LoginPage(page);
|
|
131
|
+
await loginPage.goto();
|
|
132
|
+
await loginPage.login('invalid@example.com', 'wrong');
|
|
133
|
+
|
|
134
|
+
const error = await loginPage.getErrorMessage();
|
|
135
|
+
expect(error).toContain('Invalid credentials');
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Pattern 2: Fixtures for Test Data
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// fixtures/test-data.ts
|
|
143
|
+
import { test as base } from '@playwright/test';
|
|
144
|
+
|
|
145
|
+
type TestData = {
|
|
146
|
+
testUser: {
|
|
147
|
+
email: string;
|
|
148
|
+
password: string;
|
|
149
|
+
name: string;
|
|
150
|
+
};
|
|
151
|
+
adminUser: {
|
|
152
|
+
email: string;
|
|
153
|
+
password: string;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const test = base.extend<TestData>({
|
|
158
|
+
testUser: async ({}, use) => {
|
|
159
|
+
const user = {
|
|
160
|
+
email: `test-${Date.now()}@example.com`,
|
|
161
|
+
password: process.env.TEST_USER_PASSWORD || 'DynamicTestPwd!123',
|
|
162
|
+
name: 'Test User',
|
|
163
|
+
};
|
|
164
|
+
// Setup: Create user in database
|
|
165
|
+
await createTestUser(user);
|
|
166
|
+
await use(user);
|
|
167
|
+
// Teardown: Clean up user
|
|
168
|
+
await deleteTestUser(user.email);
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
adminUser: async ({}, use) => {
|
|
172
|
+
await use({
|
|
173
|
+
email: 'admin@example.com',
|
|
174
|
+
password: process.env.ADMIN_PASSWORD!,
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Usage in tests
|
|
180
|
+
import { test } from './fixtures/test-data';
|
|
181
|
+
|
|
182
|
+
test('user can update profile', async ({ page, testUser }) => {
|
|
183
|
+
await page.goto('/login');
|
|
184
|
+
await page.getByLabel('Email').fill(testUser.email);
|
|
185
|
+
await page.getByLabel('Password').fill(testUser.password);
|
|
186
|
+
await page.getByRole('button', { name: 'Login' }).click();
|
|
187
|
+
|
|
188
|
+
await page.goto('/profile');
|
|
189
|
+
await page.getByLabel('Name').fill('Updated Name');
|
|
190
|
+
await page.getByRole('button', { name: 'Save' }).click();
|
|
191
|
+
|
|
192
|
+
await expect(page.getByText('Profile updated')).toBeVisible();
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Pattern 3: Waiting Strategies
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// ❌ Bad: Fixed timeouts
|
|
200
|
+
await page.waitForTimeout(3000); // Flaky!
|
|
201
|
+
|
|
202
|
+
// ✅ Good: Wait for specific conditions
|
|
203
|
+
await page.waitForLoadState('networkidle');
|
|
204
|
+
await page.waitForURL('/dashboard');
|
|
205
|
+
await page.waitForSelector('[data-testid="user-profile"]');
|
|
206
|
+
|
|
207
|
+
// ✅ Better: Auto-waiting with assertions
|
|
208
|
+
await expect(page.getByText('Welcome')).toBeVisible();
|
|
209
|
+
await expect(page.getByRole('button', { name: 'Submit' }))
|
|
210
|
+
.toBeEnabled();
|
|
211
|
+
|
|
212
|
+
// Wait for API response
|
|
213
|
+
const responsePromise = page.waitForResponse(
|
|
214
|
+
response => response.url().includes('/api/users') && response.status() === 200
|
|
215
|
+
);
|
|
216
|
+
await page.getByRole('button', { name: 'Load Users' }).click();
|
|
217
|
+
const response = await responsePromise;
|
|
218
|
+
const data = await response.json();
|
|
219
|
+
expect(data.users).toHaveLength(10);
|
|
220
|
+
|
|
221
|
+
// Wait for multiple conditions
|
|
222
|
+
await Promise.all([
|
|
223
|
+
page.waitForURL('/success'),
|
|
224
|
+
page.waitForLoadState('networkidle'),
|
|
225
|
+
expect(page.getByText('Payment successful')).toBeVisible(),
|
|
226
|
+
]);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Pattern 4: Network Mocking and Interception
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Mock API responses
|
|
233
|
+
test('displays error when API fails', async ({ page }) => {
|
|
234
|
+
await page.route('**/api/users', route => {
|
|
235
|
+
route.fulfill({
|
|
236
|
+
status: 500,
|
|
237
|
+
contentType: 'application/json',
|
|
238
|
+
body: JSON.stringify({ error: 'Internal Server Error' }),
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
await page.goto('/users');
|
|
243
|
+
await expect(page.getByText('Failed to load users')).toBeVisible();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Intercept and modify requests
|
|
247
|
+
test('can modify API request', async ({ page }) => {
|
|
248
|
+
await page.route('**/api/users', async route => {
|
|
249
|
+
const request = route.request();
|
|
250
|
+
const postData = JSON.parse(request.postData() || '{}');
|
|
251
|
+
|
|
252
|
+
// Modify request
|
|
253
|
+
postData.role = 'admin';
|
|
254
|
+
|
|
255
|
+
await route.continue({
|
|
256
|
+
postData: JSON.stringify(postData),
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Test continues...
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Mock third-party services
|
|
264
|
+
test('payment flow with mocked Stripe', async ({ page }) => {
|
|
265
|
+
await page.route('**/api/stripe/**', route => {
|
|
266
|
+
route.fulfill({
|
|
267
|
+
status: 200,
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
id: 'mock_payment_id',
|
|
270
|
+
status: 'succeeded',
|
|
271
|
+
}),
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Test payment flow with mocked response
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Cypress Patterns
|
|
280
|
+
|
|
281
|
+
### Setup and Configuration
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// cypress.config.ts
|
|
285
|
+
import { defineConfig } from 'cypress';
|
|
286
|
+
|
|
287
|
+
export default defineConfig({
|
|
288
|
+
e2e: {
|
|
289
|
+
baseUrl: 'http://localhost:3000',
|
|
290
|
+
viewportWidth: 1280,
|
|
291
|
+
viewportHeight: 720,
|
|
292
|
+
video: false,
|
|
293
|
+
screenshotOnRunFailure: true,
|
|
294
|
+
defaultCommandTimeout: 10000,
|
|
295
|
+
requestTimeout: 10000,
|
|
296
|
+
setupNodeEvents(on, config) {
|
|
297
|
+
// Implement node event listeners
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Pattern 1: Custom Commands
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// cypress/support/commands.ts
|
|
307
|
+
declare global {
|
|
308
|
+
namespace Cypress {
|
|
309
|
+
interface Chainable {
|
|
310
|
+
login(email: string, password: string): Chainable<void>;
|
|
311
|
+
createUser(userData: UserData): Chainable<User>;
|
|
312
|
+
dataCy(value: string): Chainable<JQuery<HTMLElement>>;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
Cypress.Commands.add('login', (email: string, password: string) => {
|
|
318
|
+
cy.visit('/login');
|
|
319
|
+
cy.get('[data-testid="email"]').type(email);
|
|
320
|
+
cy.get('[data-testid="password"]').type(password);
|
|
321
|
+
cy.get('[data-testid="login-button"]').click();
|
|
322
|
+
cy.url().should('include', '/dashboard');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
Cypress.Commands.add('createUser', (userData: UserData) => {
|
|
326
|
+
return cy.request('POST', '/api/users', userData)
|
|
327
|
+
.its('body');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
Cypress.Commands.add('dataCy', (value: string) => {
|
|
331
|
+
return cy.get(`[data-cy="${value}"]`);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Usage
|
|
335
|
+
cy.login('user@example.com', 'password');
|
|
336
|
+
cy.dataCy('submit-button').click();
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Pattern 2: Cypress Intercept
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Mock API calls
|
|
343
|
+
cy.intercept('GET', '/api/users', {
|
|
344
|
+
statusCode: 200,
|
|
345
|
+
body: [
|
|
346
|
+
{ id: 1, name: 'John' },
|
|
347
|
+
{ id: 2, name: 'Jane' },
|
|
348
|
+
],
|
|
349
|
+
}).as('getUsers');
|
|
350
|
+
|
|
351
|
+
cy.visit('/users');
|
|
352
|
+
cy.wait('@getUsers');
|
|
353
|
+
cy.get('[data-testid="user-list"]').children().should('have.length', 2);
|
|
354
|
+
|
|
355
|
+
// Modify responses
|
|
356
|
+
cy.intercept('GET', '/api/users', (req) => {
|
|
357
|
+
req.reply((res) => {
|
|
358
|
+
// Modify response
|
|
359
|
+
res.body.users = res.body.users.slice(0, 5);
|
|
360
|
+
res.send();
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// Simulate slow network
|
|
365
|
+
cy.intercept('GET', '/api/data', (req) => {
|
|
366
|
+
req.reply((res) => {
|
|
367
|
+
res.delay(3000); // 3 second delay
|
|
368
|
+
res.send();
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Advanced Patterns
|
|
374
|
+
|
|
375
|
+
### Pattern 1: Visual Regression Testing
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// With Playwright
|
|
379
|
+
import { test, expect } from '@playwright/test';
|
|
380
|
+
|
|
381
|
+
test('homepage looks correct', async ({ page }) => {
|
|
382
|
+
await page.goto('/');
|
|
383
|
+
await expect(page).toHaveScreenshot('homepage.png', {
|
|
384
|
+
fullPage: true,
|
|
385
|
+
maxDiffPixels: 100,
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test('button in all states', async ({ page }) => {
|
|
390
|
+
await page.goto('/components');
|
|
391
|
+
|
|
392
|
+
const button = page.getByRole('button', { name: 'Submit' });
|
|
393
|
+
|
|
394
|
+
// Default state
|
|
395
|
+
await expect(button).toHaveScreenshot('button-default.png');
|
|
396
|
+
|
|
397
|
+
// Hover state
|
|
398
|
+
await button.hover();
|
|
399
|
+
await expect(button).toHaveScreenshot('button-hover.png');
|
|
400
|
+
|
|
401
|
+
// Disabled state
|
|
402
|
+
await button.evaluate(el => el.setAttribute('disabled', 'true'));
|
|
403
|
+
await expect(button).toHaveScreenshot('button-disabled.png');
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Pattern 2: Parallel Testing with Sharding
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
// playwright.config.ts
|
|
411
|
+
export default defineConfig({
|
|
412
|
+
projects: [
|
|
413
|
+
{
|
|
414
|
+
name: 'shard-1',
|
|
415
|
+
use: { ...devices['Desktop Chrome'] },
|
|
416
|
+
grepInvert: /@slow/,
|
|
417
|
+
shard: { current: 1, total: 4 },
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: 'shard-2',
|
|
421
|
+
use: { ...devices['Desktop Chrome'] },
|
|
422
|
+
shard: { current: 2, total: 4 },
|
|
423
|
+
},
|
|
424
|
+
// ... more shards
|
|
425
|
+
],
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// Run in CI
|
|
429
|
+
// npx playwright test --shard=1/4
|
|
430
|
+
// npx playwright test --shard=2/4
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Pattern 3: Accessibility Testing
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
// Install: npm install @axe-core/playwright
|
|
437
|
+
import { test, expect } from '@playwright/test';
|
|
438
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
439
|
+
|
|
440
|
+
test('page should not have accessibility violations', async ({ page }) => {
|
|
441
|
+
await page.goto('/');
|
|
442
|
+
|
|
443
|
+
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
444
|
+
.exclude('#third-party-widget')
|
|
445
|
+
.analyze();
|
|
446
|
+
|
|
447
|
+
expect(accessibilityScanResults.violations).toEqual([]);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test('form is accessible', async ({ page }) => {
|
|
451
|
+
await page.goto('/signup');
|
|
452
|
+
|
|
453
|
+
const results = await new AxeBuilder({ page })
|
|
454
|
+
.include('form')
|
|
455
|
+
.analyze();
|
|
456
|
+
|
|
457
|
+
expect(results.violations).toEqual([]);
|
|
458
|
+
});
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## Best Practices
|
|
462
|
+
|
|
463
|
+
1. **Use Data Attributes**: `data-testid` or `data-cy` for stable selectors
|
|
464
|
+
2. **Avoid Brittle Selectors**: Don't rely on CSS classes or DOM structure
|
|
465
|
+
3. **Test User Behavior**: Click, type, see - not implementation details
|
|
466
|
+
4. **Keep Tests Independent**: Each test should run in isolation
|
|
467
|
+
5. **Clean Up Test Data**: Create and destroy test data in each test
|
|
468
|
+
6. **Use Page Objects**: Encapsulate page logic
|
|
469
|
+
7. **Meaningful Assertions**: Check actual user-visible behavior
|
|
470
|
+
8. **Optimize for Speed**: Mock when possible, parallel execution
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
// ❌ Bad selectors
|
|
474
|
+
cy.get('.btn.btn-primary.submit-button').click();
|
|
475
|
+
cy.get('div > form > div:nth-child(2) > input').type('text');
|
|
476
|
+
|
|
477
|
+
// ✅ Good selectors
|
|
478
|
+
cy.getByRole('button', { name: 'Submit' }).click();
|
|
479
|
+
cy.getByLabel('Email address').type('user@example.com');
|
|
480
|
+
cy.get('[data-testid="email-input"]').type('user@example.com');
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## Common Pitfalls
|
|
484
|
+
|
|
485
|
+
- **Flaky Tests**: Use proper waits, not fixed timeouts
|
|
486
|
+
- **Slow Tests**: Mock external APIs, use parallel execution
|
|
487
|
+
- **Over-Testing**: Don't test every edge case with E2E
|
|
488
|
+
- **Coupled Tests**: Tests should not depend on each other
|
|
489
|
+
- **Poor Selectors**: Avoid CSS classes and nth-child
|
|
490
|
+
- **No Cleanup**: Clean up test data after each test
|
|
491
|
+
- **Testing Implementation**: Test user behavior, not internals
|
|
492
|
+
|
|
493
|
+
## Debugging Failing Tests
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
// Playwright debugging
|
|
497
|
+
// 1. Run in headed mode
|
|
498
|
+
npx playwright test --headed
|
|
499
|
+
|
|
500
|
+
// 2. Run in debug mode
|
|
501
|
+
npx playwright test --debug
|
|
502
|
+
|
|
503
|
+
// 3. Use trace viewer
|
|
504
|
+
await page.screenshot({ path: 'screenshot.png' });
|
|
505
|
+
await page.video()?.saveAs('video.webm');
|
|
506
|
+
|
|
507
|
+
// 4. Add test.step for better reporting
|
|
508
|
+
test('checkout flow', async ({ page }) => {
|
|
509
|
+
await test.step('Add item to cart', async () => {
|
|
510
|
+
await page.goto('/products');
|
|
511
|
+
await page.getByRole('button', { name: 'Add to Cart' }).click();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
await test.step('Proceed to checkout', async () => {
|
|
515
|
+
await page.goto('/cart');
|
|
516
|
+
await page.getByRole('button', { name: 'Checkout' }).click();
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// 5. Inspect page state
|
|
521
|
+
await page.pause(); // Pauses execution, opens inspector
|
|
522
|
+
```
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: failure-autopsy-and-healing
|
|
3
|
+
description: >-
|
|
4
|
+
Automated failure triage, visual error autopsy from Screenshots/FAILED_*.png and Allure reports,
|
|
5
|
+
cross-platform JUnit XML parsing, and in-place Page Class code healing across automated test suites.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Failure Autopsy & Auto-Healing (Diagnostic & Self-Healing Engine)
|
|
9
|
+
|
|
10
|
+
Specialized skill for diagnosing batch test execution failures across automated test cases, performing visual autopsies via `Screenshots/FAILED_*.png`, parsing `JUnit_Report.xml`, and healing Page Classes (`Keywords/<namespace>/pages/*.groovy` or `pages/*.ts`) in place.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Five-Step Enterprise Autopsy Protocol
|
|
15
|
+
|
|
16
|
+
```mermaid
|
|
17
|
+
flowchart LR
|
|
18
|
+
A["1. Detect FAILED TC"] --> B["2. Inspect Screenshots/FAILED_*.png"]
|
|
19
|
+
B --> C["3. Parse JUnit_Report.xml"]
|
|
20
|
+
C --> D["4. Heal Page Class in-place"]
|
|
21
|
+
D --> E["5. Re-run suite: 100% PASS"]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
1. **Zero-Token Triage:** Pinpoint failed line and cause instantly (<50 tokens):
|
|
25
|
+
```bash
|
|
26
|
+
python3 tools/pinpoint_failure.py
|
|
27
|
+
# or bash .agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh
|
|
28
|
+
```
|
|
29
|
+
2. **Visual Autopsy:** Inspect the latest error screenshot to immediately detect: disabled buttons, missing required fields, UI overlaps, or obscuring modal backdrops.
|
|
30
|
+
3. **XML Parsing:** Extract stacktrace and exception messages from `Reports/<Timestamp>/JUnit_Report.xml` or Playwright test results.
|
|
31
|
+
4. **In-Place Healing:** Update the corresponding Page Class in `Keywords/<project_namespace>/pages/` or `pages/`.
|
|
32
|
+
5. **Regression Guard:** Verify 100% UTF-8 No BOM and re-run the targeted suite to confirm 100% Pass.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 2. Dynamic Module & Page Class Mapping Architecture
|
|
37
|
+
|
|
38
|
+
| Functional Module Category | Target Page Class to Heal | Common Failure Vector | Recommended Auto-Heal Strategy |
|
|
39
|
+
| :--- | :--- | :--- | :--- |
|
|
40
|
+
| **Authentication & Session** | `LoginPage` | Stale session, cookie expiration | Switch to zero-second storage state / token injection |
|
|
41
|
+
| **System Admin & RBAC** | `UserAdminPage` | Role permission dialog overlay | Smart wait for dialog animation + backdrop dismissal |
|
|
42
|
+
| **Master Data & Catalogs** | `CategoryPage` | Table virtualization lazy-load | Scroll into view + waitForLoadState('networkidle') |
|
|
43
|
+
| **Workflows & Approvals** | `PlanningPage` | Button disabled state / slow API | Await formControl valid state before trigger click |
|
|
44
|
+
| **Data Tables & Search** | `DataManagementPage`| Dynamic table pagination index | Target semantic cell content or unique row testid |
|
|
45
|
+
| **Reporting & Exports** | `ReportPage` | Background worker generation lag | Poll download trigger with timeout rather than static wait |
|
|
46
|
+
| **API & Integrations** | `ApiTokenPage` | Bearer token header mismatch | Validate token generation response payload schema |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 3. Reference Documentation
|
|
51
|
+
|
|
52
|
+
- [**Diagnostic & Healing Matrix (`healing_matrix.md`)**](references/healing_matrix.md): Concrete code prescriptions for disabled buttons, boundary overlaps, `ElementClickInterceptedException`, `StaleElementReferenceException`, and 4 flaky test root causes.
|
|
53
|
+
- [**Fast-Fix Triad & Runtime Helper (`tools/browser_helpers.js`)**](tools/browser_helpers.js): In-browser diagnosis engine via `window.__qa` (`audit`, `heal`, `clickSafe`, `hud`).
|
|
54
|
+
- **Automated Triage Script:** `python3 tools/pinpoint_failure.py` and `scripts/triage_failures.sh`.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Enterprise Automation Failure Diagnostic & Healing Matrix
|
|
2
|
+
|
|
3
|
+
Reference documentation for the `failure-autopsy-and-healing` skill.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Diagnostic & Healing Matrix
|
|
8
|
+
|
|
9
|
+
| Visual / Exception Symptom | Root Cause | Groovy POM Healing Patch |
|
|
10
|
+
| :--- | :--- | :--- |
|
|
11
|
+
| **Save button `[ ✓ ]` disabled / dimmed** | Angular Reactive Form missed change detection or missing required field (`*`). | `WebElement el = findElement(By.cssSelector("input[formcontrolname='code']"))`<br>`jsSetInput(el, uniqueCode)` |
|
|
12
|
+
| **Alert: *"Phạm vi giữa các khu/cụm không hợp lệ"*** | OpenLayers polygon overlaps previously saved boundary data. | `Actions actions = new Actions(driver)`<br>`actions.moveToElement(viewport, -180, -80).click().perform()` |
|
|
13
|
+
| **`ElementClickInterceptedException`** | Modal overlay `.p-component-overlay` fading out but still blocking clicks. | `((JavascriptExecutor) driver).executeScript("arguments[0].click();", element)` |
|
|
14
|
+
| **Red Toast: *"Mã đã tồn tại"*** | Non-unique test data colliding with existing records. | `String code = BasePage.uniqueCode("CCN_")` |
|
|
15
|
+
| **`StaleElementReferenceException`** | Angular DOM re-rendered after API response, detaching element. | `for (int i = 0; i < 3; i++) { try { findElement(by).click(); break; } catch (StaleElementReferenceException e) { sleep(500) } }` |
|
|
16
|
+
| **`TimeoutException` (Waiting for table/button)** | Loading overlay `.p-blockui` or spinner `.p-progress-spinner` blocking UI. | `waitForElementNotVisible(By.cssSelector(".p-blockui, .p-progress-spinner"), 10)` |
|
|
17
|
+
| **PrimeNG Dropdown value not applied** | Clicked `p-dropdown` but overlay panel `.p-dropdown-panel` has not rendered. | `waitForElementVisible(By.cssSelector(".p-dropdown-panel"), 5)`<br>`findElement(By.xpath("//li[contains(., '" + text + "')]")).click()` |
|
|
18
|
+
| **Blank Screenshot** | Browser crashed before capturing UI (OOM or lost WebDriver session). | Inspect `console0.log`, kill zombie drivers (`pkill -f chromedriver`), restart session. |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 2. Four Flaky Test Root Causes & Cures
|
|
23
|
+
|
|
24
|
+
| Flaky Root Cause | Symptom | Curing Strategy (Root Fix) |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **1. Unstable Locator** | Random failures due to CSS refactoring or dynamic hashes | Enforce Master Priority Map: switch to `[formcontrolname]`, `[data-testid]`; forbid positional XPaths. |
|
|
27
|
+
| **2. Timing & Race Condition** | Fails under network latency; clicks before Angular finishes render | Replace all hard sleeps with Smart Wait: `waitForElementVisible`, wait for `.p-blockui` / `.p-progress-spinner` to hide. |
|
|
28
|
+
| **3. Test Data Conflict** | Fails on parallel or repeated runs due to duplicate codes | Enforce `BasePage.uniqueCode(prefix)` with epoch timestamps. |
|
|
29
|
+
| **4. Environment Dependency** | Fails due to expired session or stale browser cache | Use `LoginPage.ensureLoggedIn()` before every script. |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Fast-Fix Triad (Zero-Token Diagnostic & Targeted Healing Workflow)
|
|
34
|
+
|
|
35
|
+
To maximize fix speed and minimize token consumption without degrading test fidelity, follow this 3-step loop:
|
|
36
|
+
|
|
37
|
+
### Step 1: Zero-Token Pinpoint (CLI Log Extraction)
|
|
38
|
+
Never read entire JUnit XML reports (>50KB). Extract failed test cases, exception reasons, and file line numbers in <50 tokens:
|
|
39
|
+
```bash
|
|
40
|
+
python3 tools/pinpoint_failure.py Reports/<timestamp>/<suite>/JUnit_Report.xml
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Step 2: Instant Form & DOM Audit (Playwright MCP Recipe 15)
|
|
44
|
+
When a test fails because a button is disabled (e.g. Save `[ ✓ ]`), never guess which field is invalid. Execute Recipe 15 via `browser_run_code_unsafe` on the active live page:
|
|
45
|
+
```javascript
|
|
46
|
+
() => {
|
|
47
|
+
const invalids = Array.from(document.querySelectorAll('form .ng-invalid, input.ng-invalid, p-dropdown.ng-invalid'));
|
|
48
|
+
return invalids.map(el => ({
|
|
49
|
+
tag: el.tagName,
|
|
50
|
+
name: el.getAttribute('name') || el.getAttribute('formcontrolname') || el.id,
|
|
51
|
+
classes: el.className
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
This instantly highlights missing required inputs, pattern mismatches, or invalid dropdowns.
|
|
56
|
+
|
|
57
|
+
### Step 3: Targeted Single-TC Re-test (Zero Waste)
|
|
58
|
+
Never rerun an entire Test Suite to verify a 1-line locator or timing fix. Rerun only the targeted test case:
|
|
59
|
+
```bash
|
|
60
|
+
# Via KRE CLI (License mode):
|
|
61
|
+
katalonc -noSplash -runMode=console -projectPath="." -retry=0 -testCasePath="Test Cases/TC-xxx" -browserType="Chrome (headless)"
|
|
62
|
+
|
|
63
|
+
# Or verify live flow in 2 seconds via Playwright MCP script.
|
|
64
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# QA Investigation & Root-Cause Analysis Protocol
|
|
2
|
+
|
|
3
|
+
*(Inherited from `fugazi/test-automation-skills-agents`)*
|
|
4
|
+
|
|
5
|
+
A file-backed, evidence-first investigation journal protocol for diagnosing failing and flaky automation test cases in enterprise automation projects.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Core Principles
|
|
10
|
+
|
|
11
|
+
1. **Context Window is Volatile RAM; Filesystem is Persistent Disk:**
|
|
12
|
+
- Long triage sessions cause context drift. All goals, hypotheses, and evidence must be logged in markdown.
|
|
13
|
+
2. **Discovered Classification, Not Presumed:**
|
|
14
|
+
- Never assume upfront whether a failure is a Flaky test, an AUT Defect, or a Test Infrastructure issue. The classification is a finding discovered through evidence.
|
|
15
|
+
3. **No Guesswork / No Blind Fixes:**
|
|
16
|
+
- Every fix must address a proven root cause, not a symptom.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. Four-Phase Investigation Cycle
|
|
21
|
+
|
|
22
|
+
```mermaid
|
|
23
|
+
flowchart TD
|
|
24
|
+
P1["<b>Phase 1: Reproduction & Triage</b><br/>• Run single-TC isolated re-test<br/>• Classify: Intermittent (Flaky) vs Deterministic (Bug)"]
|
|
25
|
+
P2["<b>Phase 2: Evidence Collection</b><br/>• Extract CLI logs & stack trace<br/>• Inspect Screenshots/FAILED_*.png<br/>• Capture network & timing data"]
|
|
26
|
+
P3["<b>Phase 3: Hypothesis & Root Cause</b><br/>• Form testable hypothesis<br/>• Isolate: Timing vs Stale DOM vs AUT defect"]
|
|
27
|
+
P4["<b>Phase 4: Targeted Fix & Validation</b><br/>• Apply Clean POM Page Class fix<br/>• Validate 3x consecutive runs<br/>• Run verify_all.py DoD check"]
|
|
28
|
+
|
|
29
|
+
P1 --> P2 --> P3 --> P4
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Phase 1: Reproduction & Triage
|
|
33
|
+
- Run the failing test in isolation using the targeted single-TC runner.
|
|
34
|
+
- Determine failure pattern:
|
|
35
|
+
- **Deterministic Bug:** Fails 100% of the time with identical stack trace.
|
|
36
|
+
- **Intermittent Flaky:** Passes sometimes, fails under load/timing variations.
|
|
37
|
+
- **Environment Blocker:** Fails on 401/403 session expiration or network timeout.
|
|
38
|
+
|
|
39
|
+
### Phase 2: Evidence Collection
|
|
40
|
+
- Collect ground-truth artifacts:
|
|
41
|
+
1. CLI log excerpt around the failure point.
|
|
42
|
+
2. Failure screenshot in `Screenshots/FAILED_<TestCaseId>_*.png`.
|
|
43
|
+
3. Form audit state (e.g. disabled Save button, red validation borders).
|
|
44
|
+
4. Redact any sensitive credentials or personal tokens.
|
|
45
|
+
|
|
46
|
+
### Phase 3: Hypothesis Formulation
|
|
47
|
+
- Check the 4 most common failure archetypes:
|
|
48
|
+
- **Archetype A (PrimeNG Toast Collision):** Previous toast covers the button. Cure: `dismissToast()`.
|
|
49
|
+
- **Archetype B (Stale Element / Angular Re-render):** DOM re-created mid-click. Cure: Re-fetch locator with `BasePage.x()`.
|
|
50
|
+
- **Archetype C (Dialog Backdrop Overlay):** `cdk-overlay-backdrop` blocks click. Cure: Wait for backdrop fade-out.
|
|
51
|
+
- **Archetype D (Real AUT Defect):** Backend HTTP 500 or constraint violation. Cure: Log to `KNOWN_ISSUES.md`.
|
|
52
|
+
|
|
53
|
+
### Phase 4: Targeted Fix & Validation
|
|
54
|
+
- Implement fix exclusively inside Page Classes in `Keywords/<namespace>/pages/` or `pages/` (never in `Scripts/`).
|
|
55
|
+
- Validate stability across multiple runs.
|
|
56
|
+
- Run `python3 tools/verify_all.py` to confirm zero regressions.
|