@patricio0312rev/skillset 0.1.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/CHANGELOG.md +29 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/bin/cli.js +37 -0
- package/package.json +55 -0
- package/src/commands/init.js +301 -0
- package/src/index.js +168 -0
- package/src/lib/config.js +200 -0
- package/src/lib/generator.js +166 -0
- package/src/utils/display.js +95 -0
- package/src/utils/readme.js +196 -0
- package/src/utils/tool-specific.js +233 -0
- package/templates/ai-engineering/agent-orchestration-planner/ SKILL.md +266 -0
- package/templates/ai-engineering/cost-latency-optimizer/ SKILL.md +270 -0
- package/templates/ai-engineering/doc-to-vector-dataset-generator/ SKILL.md +239 -0
- package/templates/ai-engineering/evaluation-harness/ SKILL.md +219 -0
- package/templates/ai-engineering/guardrails-safety-filter-builder/ SKILL.md +226 -0
- package/templates/ai-engineering/llm-debugger/ SKILL.md +283 -0
- package/templates/ai-engineering/prompt-regression-tester/ SKILL.md +216 -0
- package/templates/ai-engineering/prompt-template-builder/ SKILL.md +393 -0
- package/templates/ai-engineering/rag-pipeline-builder/ SKILL.md +244 -0
- package/templates/ai-engineering/tool-function-schema-designer/ SKILL.md +219 -0
- package/templates/architecture/adr-writer/ SKILL.md +250 -0
- package/templates/architecture/api-versioning-deprecation-planner/ SKILL.md +331 -0
- package/templates/architecture/domain-model-boundaries-mapper/ SKILL.md +300 -0
- package/templates/architecture/migration-planner/ SKILL.md +376 -0
- package/templates/architecture/performance-budget-setter/ SKILL.md +318 -0
- package/templates/architecture/reliability-strategy-builder/ SKILL.md +286 -0
- package/templates/architecture/rfc-generator/ SKILL.md +362 -0
- package/templates/architecture/scalability-playbook/ SKILL.md +279 -0
- package/templates/architecture/system-design-generator/ SKILL.md +339 -0
- package/templates/architecture/tech-debt-prioritizer/ SKILL.md +329 -0
- package/templates/backend/api-contract-normalizer/ SKILL.md +487 -0
- package/templates/backend/api-endpoint-generator/ SKILL.md +415 -0
- package/templates/backend/auth-module-builder/ SKILL.md +99 -0
- package/templates/backend/background-jobs-designer/ SKILL.md +166 -0
- package/templates/backend/caching-strategist/ SKILL.md +190 -0
- package/templates/backend/error-handling-standardizer/ SKILL.md +174 -0
- package/templates/backend/rate-limiting-abuse-protection/ SKILL.md +147 -0
- package/templates/backend/rbac-permissions-builder/ SKILL.md +158 -0
- package/templates/backend/service-layer-extractor/ SKILL.md +269 -0
- package/templates/backend/webhook-receiver-hardener/ SKILL.md +211 -0
- package/templates/ci-cd/artifact-sbom-publisher/ SKILL.md +236 -0
- package/templates/ci-cd/caching-strategy-optimizer/ SKILL.md +195 -0
- package/templates/ci-cd/deployment-checklist-generator/ SKILL.md +381 -0
- package/templates/ci-cd/github-actions-pipeline-creator/ SKILL.md +348 -0
- package/templates/ci-cd/monorepo-ci-optimizer/ SKILL.md +298 -0
- package/templates/ci-cd/preview-environments-builder/ SKILL.md +187 -0
- package/templates/ci-cd/quality-gates-enforcer/ SKILL.md +342 -0
- package/templates/ci-cd/release-automation-builder/ SKILL.md +281 -0
- package/templates/ci-cd/rollback-workflow-builder/ SKILL.md +372 -0
- package/templates/ci-cd/secrets-env-manager/ SKILL.md +242 -0
- package/templates/db-management/backup-restore-runbook-generator/ SKILL.md +505 -0
- package/templates/db-management/data-integrity-auditor/ SKILL.md +505 -0
- package/templates/db-management/data-retention-archiving-planner/ SKILL.md +430 -0
- package/templates/db-management/data-seeding-fixtures-builder/ SKILL.md +375 -0
- package/templates/db-management/db-performance-watchlist/ SKILL.md +425 -0
- package/templates/db-management/etl-sync-job-builder/ SKILL.md +457 -0
- package/templates/db-management/multi-tenant-safety-checker/ SKILL.md +398 -0
- package/templates/db-management/prisma-migration-assistant/ SKILL.md +379 -0
- package/templates/db-management/schema-consistency-checker/ SKILL.md +440 -0
- package/templates/db-management/sql-query-optimizer/ SKILL.md +324 -0
- package/templates/foundation/changelog-writer/ SKILL.md +431 -0
- package/templates/foundation/code-formatter-installer/ SKILL.md +320 -0
- package/templates/foundation/codebase-summarizer/ SKILL.md +360 -0
- package/templates/foundation/dependency-doctor/ SKILL.md +163 -0
- package/templates/foundation/dev-environment-bootstrapper/ SKILL.md +259 -0
- package/templates/foundation/dev-onboarding-builder/ SKILL.md +556 -0
- package/templates/foundation/docs-starter-kit/ SKILL.md +574 -0
- package/templates/foundation/explaining-code/SKILL.md +13 -0
- package/templates/foundation/git-hygiene-enforcer/ SKILL.md +455 -0
- package/templates/foundation/project-scaffolder/ SKILL.md +65 -0
- package/templates/foundation/project-scaffolder/references/templates.md +126 -0
- package/templates/foundation/repo-structure-linter/ SKILL.md +0 -0
- package/templates/foundation/repo-structure-linter/references/conventions.md +98 -0
- package/templates/frontend/animation-micro-interaction-pack/ SKILL.md +41 -0
- package/templates/frontend/component-scaffold-generator/ SKILL.md +562 -0
- package/templates/frontend/design-to-component-translator/ SKILL.md +547 -0
- package/templates/frontend/form-wizard-builder/ SKILL.md +553 -0
- package/templates/frontend/frontend-refactor-planner/ SKILL.md +37 -0
- package/templates/frontend/i18n-frontend-implementer/ SKILL.md +44 -0
- package/templates/frontend/modal-drawer-system/ SKILL.md +377 -0
- package/templates/frontend/page-layout-builder/ SKILL.md +630 -0
- package/templates/frontend/state-ux-flow-builder/ SKILL.md +23 -0
- package/templates/frontend/table-builder/ SKILL.md +350 -0
- package/templates/performance/alerting-dashboard-builder/ SKILL.md +162 -0
- package/templates/performance/backend-latency-profiler-helper/ SKILL.md +108 -0
- package/templates/performance/caching-cdn-strategy-planner/ SKILL.md +150 -0
- package/templates/performance/capacity-planning-helper/ SKILL.md +242 -0
- package/templates/performance/core-web-vitals-tuner/ SKILL.md +126 -0
- package/templates/performance/incident-runbook-generator/ SKILL.md +162 -0
- package/templates/performance/load-test-scenario-builder/ SKILL.md +256 -0
- package/templates/performance/observability-setup/ SKILL.md +232 -0
- package/templates/performance/postmortem-writer/ SKILL.md +203 -0
- package/templates/performance/structured-logging-standardizer/ SKILL.md +122 -0
- package/templates/security/auth-security-reviewer/ SKILL.md +428 -0
- package/templates/security/dependency-vulnerability-triage/ SKILL.md +495 -0
- package/templates/security/input-validation-sanitization-auditor/ SKILL.md +76 -0
- package/templates/security/pii-redaction-logging-policy-builder/ SKILL.md +65 -0
- package/templates/security/rbac-policy-tester/ SKILL.md +80 -0
- package/templates/security/secrets-scanner/ SKILL.md +462 -0
- package/templates/security/secure-headers-csp-builder/ SKILL.md +404 -0
- package/templates/security/security-incident-playbook-generator/ SKILL.md +76 -0
- package/templates/security/security-pr-checklist-skill/ SKILL.md +62 -0
- package/templates/security/threat-model-generator/ SKILL.md +394 -0
- package/templates/testing/contract-testing-builder/ SKILL.md +492 -0
- package/templates/testing/coverage-strategist/ SKILL.md +436 -0
- package/templates/testing/e2e-test-builder/ SKILL.md +382 -0
- package/templates/testing/flaky-test-detective/ SKILL.md +416 -0
- package/templates/testing/integration-test-builder/ SKILL.md +525 -0
- package/templates/testing/mocking-assistant/ SKILL.md +383 -0
- package/templates/testing/snapshot-test-refactorer/ SKILL.md +375 -0
- package/templates/testing/test-data-factory-builder/ SKILL.md +449 -0
- package/templates/testing/test-reporting-triage-skill/ SKILL.md +469 -0
- package/templates/testing/unit-test-generator/ SKILL.md +548 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flaky-test-detective
|
|
3
|
+
description: Diagnoses and fixes flaky tests by identifying root causes (timing issues, shared state, randomness, network dependencies) and provides stabilization strategies. Use for "flaky tests", "test stability", "intermittent failures", or "test debugging".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Flaky Test Detective
|
|
7
|
+
|
|
8
|
+
Diagnose and eliminate flaky tests systematically.
|
|
9
|
+
|
|
10
|
+
## Common Flaky Test Patterns
|
|
11
|
+
|
|
12
|
+
### 1. Timing Issues
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
// ❌ Flaky: Race condition
|
|
16
|
+
test("should load user data", async () => {
|
|
17
|
+
render(<UserProfile userId="123" />);
|
|
18
|
+
|
|
19
|
+
// Race condition - might pass or fail
|
|
20
|
+
expect(screen.getByText("John Doe")).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// ✅ Fixed: Wait for element
|
|
24
|
+
test("should load user data", async () => {
|
|
25
|
+
render(<UserProfile userId="123" />);
|
|
26
|
+
|
|
27
|
+
await waitFor(() => {
|
|
28
|
+
expect(screen.getByText("John Doe")).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ❌ Flaky: Fixed timeout
|
|
33
|
+
test("should complete animation", async () => {
|
|
34
|
+
render(<AnimatedComponent />);
|
|
35
|
+
await new Promise((resolve) => setTimeout(resolve, 500)); // Brittle!
|
|
36
|
+
expect(element).toHaveClass("animated");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// ✅ Fixed: Wait for condition
|
|
40
|
+
test("should complete animation", async () => {
|
|
41
|
+
render(<AnimatedComponent />);
|
|
42
|
+
await waitFor(
|
|
43
|
+
() => {
|
|
44
|
+
expect(element).toHaveClass("animated");
|
|
45
|
+
},
|
|
46
|
+
{ timeout: 2000 }
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Shared State
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// ❌ Flaky: Global state pollution
|
|
55
|
+
let userId = "123";
|
|
56
|
+
|
|
57
|
+
test("test A", () => {
|
|
58
|
+
userId = "456"; // Modifies global
|
|
59
|
+
// ...
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("test B", () => {
|
|
63
|
+
expect(userId).toBe("123"); // Fails if test A runs first!
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ✅ Fixed: Isolated state
|
|
67
|
+
test("test A", () => {
|
|
68
|
+
const userId = "456"; // Local variable
|
|
69
|
+
// ...
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("test B", () => {
|
|
73
|
+
const userId = "123";
|
|
74
|
+
expect(userId).toBe("123");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// ❌ Flaky: Database not cleaned
|
|
78
|
+
test("should create user", async () => {
|
|
79
|
+
await db.user.create({ email: "test@example.com" });
|
|
80
|
+
// No cleanup!
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("should create another user", async () => {
|
|
84
|
+
await db.user.create({ email: "test@example.com" }); // Fails! Duplicate
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ✅ Fixed: Proper cleanup
|
|
88
|
+
afterEach(async () => {
|
|
89
|
+
await db.user.deleteMany();
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Randomness
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// ❌ Flaky: Random data
|
|
97
|
+
test("should sort users", () => {
|
|
98
|
+
const users = generateRandomUsers(10); // Different each time!
|
|
99
|
+
const sorted = sortUsers(users);
|
|
100
|
+
expect(sorted[0].name).toBe("Alice"); // Might not be Alice
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ✅ Fixed: Deterministic data
|
|
104
|
+
test("should sort users", () => {
|
|
105
|
+
const users = [
|
|
106
|
+
{ name: "Charlie", age: 30 },
|
|
107
|
+
{ name: "Alice", age: 25 },
|
|
108
|
+
{ name: "Bob", age: 35 },
|
|
109
|
+
];
|
|
110
|
+
const sorted = sortUsers(users);
|
|
111
|
+
expect(sorted[0].name).toBe("Alice");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ✅ Fixed: Seeded randomness
|
|
115
|
+
import { faker } from "@faker-js/faker";
|
|
116
|
+
|
|
117
|
+
beforeEach(() => {
|
|
118
|
+
faker.seed(12345); // Same data every time
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 4. Network Dependencies
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// ❌ Flaky: Real API call
|
|
126
|
+
test("should fetch users", async () => {
|
|
127
|
+
const users = await fetchUsers(); // External API!
|
|
128
|
+
expect(users).toHaveLength(10); // Might fail if API down
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ✅ Fixed: Mocked API
|
|
132
|
+
test("should fetch users", async () => {
|
|
133
|
+
server.use(
|
|
134
|
+
http.get("/api/users", () => {
|
|
135
|
+
return HttpResponse.json([
|
|
136
|
+
{ id: "1", name: "User 1" },
|
|
137
|
+
{ id: "2", name: "User 2" },
|
|
138
|
+
]);
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const users = await fetchUsers();
|
|
143
|
+
expect(users).toHaveLength(2);
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Flaky Test Detection Script
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// scripts/detect-flaky-tests.ts
|
|
151
|
+
import { execSync } from "child_process";
|
|
152
|
+
|
|
153
|
+
async function detectFlakyTests(iterations: number = 10) {
|
|
154
|
+
const results = new Map<string, { passed: number; failed: number }>();
|
|
155
|
+
|
|
156
|
+
for (let i = 0; i < iterations; i++) {
|
|
157
|
+
console.log(`\nRun ${i + 1}/${iterations}`);
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const output = execSync("npm test -- --reporter=json", {
|
|
161
|
+
encoding: "utf-8",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const testResults = JSON.parse(output);
|
|
165
|
+
|
|
166
|
+
testResults.testResults.forEach((file: any) => {
|
|
167
|
+
file.assertionResults.forEach((test: any) => {
|
|
168
|
+
const key = `${file.name}::${test.fullName}`;
|
|
169
|
+
const stats = results.get(key) || { passed: 0, failed: 0 };
|
|
170
|
+
|
|
171
|
+
if (test.status === "passed") {
|
|
172
|
+
stats.passed++;
|
|
173
|
+
} else {
|
|
174
|
+
stats.failed++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
results.set(key, stats);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error("Test run failed:", error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Analyze results
|
|
186
|
+
console.log("\n🔍 Flaky Test Report\n");
|
|
187
|
+
|
|
188
|
+
const flakyTests: string[] = [];
|
|
189
|
+
|
|
190
|
+
results.forEach((stats, testName) => {
|
|
191
|
+
if (stats.failed > 0 && stats.passed > 0) {
|
|
192
|
+
const failureRate = (stats.failed / iterations) * 100;
|
|
193
|
+
console.log(`❌ FLAKY: ${testName}`);
|
|
194
|
+
console.log(` Passed: ${stats.passed}/${iterations}`);
|
|
195
|
+
console.log(` Failed: ${stats.failed}/${iterations}`);
|
|
196
|
+
console.log(` Failure rate: ${failureRate.toFixed(1)}%\n`);
|
|
197
|
+
flakyTests.push(testName);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (flakyTests.length === 0) {
|
|
202
|
+
console.log("✅ No flaky tests detected!");
|
|
203
|
+
} else {
|
|
204
|
+
console.log(`\n🚨 Found ${flakyTests.length} flaky tests`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
detectFlakyTests(20); // Run tests 20 times
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Root Cause Analysis
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Framework for analyzing flaky tests
|
|
216
|
+
interface FlakyTestAnalysis {
|
|
217
|
+
testName: string;
|
|
218
|
+
failureRate: number;
|
|
219
|
+
symptoms: string[];
|
|
220
|
+
rootCause: "timing" | "state" | "randomness" | "network" | "unknown";
|
|
221
|
+
recommendation: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function analyzeTest(
|
|
225
|
+
testName: string,
|
|
226
|
+
errorMessages: string[]
|
|
227
|
+
): FlakyTestAnalysis {
|
|
228
|
+
const analysis: FlakyTestAnalysis = {
|
|
229
|
+
testName,
|
|
230
|
+
failureRate: 0,
|
|
231
|
+
symptoms: [],
|
|
232
|
+
rootCause: "unknown",
|
|
233
|
+
recommendation: "",
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// Detect timing issues
|
|
237
|
+
if (
|
|
238
|
+
errorMessages.some(
|
|
239
|
+
(msg) => msg.includes("timeout") || msg.includes("not found")
|
|
240
|
+
)
|
|
241
|
+
) {
|
|
242
|
+
analysis.symptoms.push("Timeout or element not found");
|
|
243
|
+
analysis.rootCause = "timing";
|
|
244
|
+
analysis.recommendation =
|
|
245
|
+
"Add explicit waits using waitFor() or findBy* queries";
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Detect shared state
|
|
249
|
+
if (
|
|
250
|
+
errorMessages.some(
|
|
251
|
+
(msg) =>
|
|
252
|
+
msg.includes("already exists") || msg.includes("unique constraint")
|
|
253
|
+
)
|
|
254
|
+
) {
|
|
255
|
+
analysis.symptoms.push("Duplicate or existing data");
|
|
256
|
+
analysis.rootCause = "state";
|
|
257
|
+
analysis.recommendation =
|
|
258
|
+
"Add beforeEach/afterEach cleanup or use unique test data";
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Detect randomness
|
|
262
|
+
if (
|
|
263
|
+
errorMessages.some(
|
|
264
|
+
(msg) => msg.includes("expected") && msg.includes("received")
|
|
265
|
+
)
|
|
266
|
+
) {
|
|
267
|
+
analysis.symptoms.push("Inconsistent values");
|
|
268
|
+
analysis.rootCause = "randomness";
|
|
269
|
+
analysis.recommendation =
|
|
270
|
+
"Use deterministic test data or seed random generators";
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Detect network issues
|
|
274
|
+
if (
|
|
275
|
+
errorMessages.some(
|
|
276
|
+
(msg) => msg.includes("network") || msg.includes("ECONNREFUSED")
|
|
277
|
+
)
|
|
278
|
+
) {
|
|
279
|
+
analysis.symptoms.push("Network or connection errors");
|
|
280
|
+
analysis.rootCause = "network";
|
|
281
|
+
analysis.recommendation = "Mock all network requests using MSW or similar";
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return analysis;
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Stabilization Guidelines
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// Test stability checklist
|
|
292
|
+
const stabilityChecklist = {
|
|
293
|
+
timing: [
|
|
294
|
+
"Use waitFor() instead of fixed timeouts",
|
|
295
|
+
"Use findBy* queries (built-in waiting)",
|
|
296
|
+
"Set appropriate timeout values",
|
|
297
|
+
"Wait for loading states to disappear",
|
|
298
|
+
],
|
|
299
|
+
state: [
|
|
300
|
+
"Clear database before each test",
|
|
301
|
+
"Reset mocks after each test",
|
|
302
|
+
"Use test-specific data (unique IDs)",
|
|
303
|
+
"Avoid global variables",
|
|
304
|
+
],
|
|
305
|
+
randomness: [
|
|
306
|
+
"Use fixed seed for random generators",
|
|
307
|
+
"Use deterministic test data",
|
|
308
|
+
"Avoid Date.now() - mock time instead",
|
|
309
|
+
"Generate IDs deterministically",
|
|
310
|
+
],
|
|
311
|
+
network: [
|
|
312
|
+
"Mock all API calls",
|
|
313
|
+
"Use MSW for HTTP mocking",
|
|
314
|
+
"Avoid real external services",
|
|
315
|
+
"Test network errors explicitly",
|
|
316
|
+
],
|
|
317
|
+
parallelism: [
|
|
318
|
+
"Use isolated databases per test worker",
|
|
319
|
+
"Avoid port conflicts (random ports)",
|
|
320
|
+
"Dont share file system state",
|
|
321
|
+
"Use test.concurrent cautiously",
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Auto-Fix Patterns
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// Automated fixes for common issues
|
|
330
|
+
|
|
331
|
+
// Fix 1: Add waitFor to assertions
|
|
332
|
+
function addWaitFor(code: string): string {
|
|
333
|
+
// Replace: expect(screen.getByText('...')).toBeInTheDocument()
|
|
334
|
+
// With: await waitFor(() => expect(screen.getByText('...')).toBeInTheDocument())
|
|
335
|
+
|
|
336
|
+
return code
|
|
337
|
+
.replace(
|
|
338
|
+
/expect\(screen\.getBy/g,
|
|
339
|
+
"await waitFor(() => expect(screen.getBy"
|
|
340
|
+
)
|
|
341
|
+
.replace(/\)\.toBeInTheDocument\(\)/g, ").toBeInTheDocument())");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Fix 2: Replace getBy with findBy
|
|
345
|
+
function replaceGetByWithFindBy(code: string): string {
|
|
346
|
+
return code.replace(/screen\.getBy/g, "await screen.findBy");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Fix 3: Add cleanup
|
|
350
|
+
function addCleanup(code: string): string {
|
|
351
|
+
if (!code.includes("afterEach")) {
|
|
352
|
+
const insertPoint = code.indexOf("test(");
|
|
353
|
+
return (
|
|
354
|
+
code.slice(0, insertPoint) +
|
|
355
|
+
"afterEach(async () => {\n await cleanup();\n});\n\n" +
|
|
356
|
+
code.slice(insertPoint)
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return code;
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Monitoring Flaky Tests in CI
|
|
364
|
+
|
|
365
|
+
```yaml
|
|
366
|
+
# .github/workflows/test-stability.yml
|
|
367
|
+
name: Test Stability
|
|
368
|
+
|
|
369
|
+
on:
|
|
370
|
+
schedule:
|
|
371
|
+
- cron: "0 2 * * *" # Run nightly
|
|
372
|
+
|
|
373
|
+
jobs:
|
|
374
|
+
stability-check:
|
|
375
|
+
runs-on: ubuntu-latest
|
|
376
|
+
steps:
|
|
377
|
+
- uses: actions/checkout@v4
|
|
378
|
+
|
|
379
|
+
- uses: actions/setup-node@v4
|
|
380
|
+
with:
|
|
381
|
+
node-version: "20"
|
|
382
|
+
|
|
383
|
+
- run: npm ci
|
|
384
|
+
|
|
385
|
+
- name: Run tests 20 times
|
|
386
|
+
run: |
|
|
387
|
+
for i in {1..20}; do
|
|
388
|
+
echo "Run $i/20"
|
|
389
|
+
npm test || echo "FAILED: Run $i"
|
|
390
|
+
done
|
|
391
|
+
|
|
392
|
+
- name: Analyze results
|
|
393
|
+
run: npm run detect-flaky-tests
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Best Practices
|
|
397
|
+
|
|
398
|
+
1. **Explicit waits**: Never use sleep/timeout
|
|
399
|
+
2. **Clean state**: Reset between tests
|
|
400
|
+
3. **Deterministic data**: No randomness
|
|
401
|
+
4. **Mock external deps**: APIs, time, randomness
|
|
402
|
+
5. **Run tests multiple times**: Catch intermittent failures
|
|
403
|
+
6. **Isolate tests**: No shared state
|
|
404
|
+
7. **Monitor CI**: Track flaky test trends
|
|
405
|
+
|
|
406
|
+
## Output Checklist
|
|
407
|
+
|
|
408
|
+
- [ ] Common patterns identified
|
|
409
|
+
- [ ] Root cause analysis performed
|
|
410
|
+
- [ ] Timing issues fixed (waitFor)
|
|
411
|
+
- [ ] Shared state eliminated (cleanup)
|
|
412
|
+
- [ ] Randomness removed (fixed seeds)
|
|
413
|
+
- [ ] Network mocked (MSW)
|
|
414
|
+
- [ ] Detection script implemented
|
|
415
|
+
- [ ] Stabilization guidelines documented
|
|
416
|
+
- [ ] CI monitoring configured
|