omgkit 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  3. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  4. package/plugin/skills/databases/redis/SKILL.md +53 -860
  5. package/plugin/skills/devops/aws/SKILL.md +68 -672
  6. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  7. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  8. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  9. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  10. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  12. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  14. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  15. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  16. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  17. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  18. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  19. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  20. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  21. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  22. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  23. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  24. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  25. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  26. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  27. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  28. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  29. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  30. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  31. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  32. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  33. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  34. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  35. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  36. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  37. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  38. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  39. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  40. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  41. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  42. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  43. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  44. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  45. package/plugin/skills/security/oauth/SKILL.md +80 -934
  46. package/plugin/skills/security/owasp/SKILL.md +78 -862
  47. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  48. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  49. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  50. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  51. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  52. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  53. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  54. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  55. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,141 +1,107 @@
1
1
  ---
2
- name: omega-testing
3
- description: Comprehensive testing strategies covering all dimensions - accuracy, performance, security, and accessibility
2
+ name: testing-omega-quality
3
+ description: Implements comprehensive testing across all quality dimensions - accuracy, performance, security, and accessibility. Use when building test strategies or ensuring production-grade quality assurance.
4
4
  category: omega
5
5
  triggers:
6
6
  - omega testing
7
7
  - comprehensive testing
8
8
  - test strategy
9
9
  - quality assurance
10
- - test pyramid
11
- - test coverage
12
- - testing best practices
13
10
  ---
14
11
 
15
- # Omega Testing
12
+ # Testing Omega Quality
16
13
 
17
- Master **comprehensive testing strategies** that cover all quality dimensions - accuracy, performance, security, and accessibility. This skill provides frameworks for building confidence in your code through systematic, thorough testing.
14
+ Master **comprehensive testing strategies** covering all quality dimensions - accuracy, performance, security, and accessibility.
18
15
 
19
- ## Purpose
16
+ ## Quick Start
20
17
 
21
- Achieve Omega-level quality assurance:
22
-
23
- - Design test strategies that catch bugs before production
24
- - Build layered test suites (unit, integration, E2E)
25
- - Test all quality dimensions, not just functionality
26
- - Create maintainable, fast, and reliable tests
27
- - Implement property-based and mutation testing
28
- - Achieve meaningful coverage metrics
29
- - Enable confident refactoring and deployment
30
-
31
- ## Features
32
-
33
- ### 1. The Omega Testing Pyramid
34
-
35
- ```markdown
36
- ## Multi-Dimensional Testing Pyramid
37
-
38
- ┌─────────────────────────────────────────────────────────────────────────┐
39
- │ OMEGA TESTING PYRAMID │
40
- ├─────────────────────────────────────────────────────────────────────────┤
41
- │ │
42
- │ /\ │
43
- │ /E2E\ ← Critical paths only │
44
- │ /─────\ Slowest, most expensive │
45
- │ / \ │
46
- │ / Visual \ ← Screenshot comparisons │
47
- │ /───────────\ │
48
- │ / \ │
49
- │ / Integration \ ← Service boundaries │
50
- │ /─────────────────\ API contracts │
51
- │ / \ │
52
- │ / Component \ ← UI components isolated │
53
- │ /───────────────────────\ │
54
- │ / \ │
55
- │ / Unit \ ← Fast, isolated │
56
- │ /─────────────────────────────\ Business logic │
57
- │ │
58
- │ Target Coverage by Layer: │
59
- │ • Unit: 80%+ (pure functions, business logic) │
60
- │ • Component: 70%+ (UI components with mocks) │
61
- │ • Integration: 60%+ (API endpoints, data flow) │
62
- │ • E2E: Critical paths 100% (happy paths, auth, checkout) │
63
- │ │
64
- └─────────────────────────────────────────────────────────────────────────┘
18
+ ```yaml
19
+ # 1. Define test strategy with 4 dimensions
20
+ TestStrategy:
21
+ Accuracy: { unit: 80%, integration: 60%, e2e: "critical paths" }
22
+ Performance: { p95: "<200ms", concurrent: 50 }
23
+ Security: { injection: true, auth: true, xss: true }
24
+ Accessibility: { wcag: "2.1 AA", keyboard: true }
25
+
26
+ # 2. Follow the test pyramid
27
+ Pyramid:
28
+ Unit: 80% # Fast, isolated, business logic
29
+ Component: 70% # UI components with mocks
30
+ Integration: 60% # API endpoints, data flow
31
+ E2E: "critical" # Happy paths, auth, checkout
32
+
33
+ # 3. Run quality gates in CI
34
+ Gates: ["coverage > 80%", "no-security-issues", "a11y-pass"]
65
35
  ```
66
36
 
67
- ### 2. Four Dimensions of Quality Testing
68
-
69
- ```typescript
70
- /**
71
- * Omega Testing covers ALL quality dimensions
72
- * Don't just test functionality - test quality
73
- */
37
+ ## Features
74
38
 
75
- type QualityDimension =
76
- | 'accuracy' // Does it work correctly?
77
- | 'performance' // Does it work fast enough?
78
- | 'security' // Is it safe from attacks?
79
- | 'accessibility'; // Can everyone use it?
39
+ | Feature | Description | Guide |
40
+ |---------|-------------|-------|
41
+ | 4D Testing | Accuracy, Performance, Security, Accessibility | Cover all quality dimensions |
42
+ | Test Pyramid | Unit, Component, Integration, E2E layers | More units, fewer E2E |
43
+ | Property-Based | Test with generated inputs | Catch edge cases automatically |
44
+ | Performance | Response time, load, memory testing | Percentile-based thresholds |
45
+ | Security | SQL injection, XSS, auth bypass tests | OWASP-aligned coverage |
46
+ | Accessibility | WCAG compliance, keyboard, screen reader | Automated a11y scanning |
47
+ | Visual Regression | Screenshot comparison testing | Catch UI regressions |
80
48
 
81
- interface OmegaTestSuite {
82
- accuracy: AccuracyTests;
83
- performance: PerformanceTests;
84
- security: SecurityTests;
85
- accessibility: AccessibilityTests;
86
- }
49
+ ## Common Patterns
87
50
 
88
- // Dimension 1: Accuracy (Functional Correctness)
89
- interface AccuracyTests {
90
- happyPath: Test[]; // Normal use cases work
91
- edgeCases: Test[]; // Boundary conditions handled
92
- errorCases: Test[]; // Failures handled gracefully
93
- regressions: Test[]; // Previously fixed bugs stay fixed
94
- }
51
+ ### The Omega Test Pyramid
95
52
 
96
- // Dimension 2: Performance
97
- interface PerformanceTests {
98
- responseTime: Test[]; // Operations complete in time
99
- throughput: Test[]; // System handles expected load
100
- memory: Test[]; // No memory leaks
101
- scalability: Test[]; // Performance under scale
102
- }
53
+ ```
54
+ /\
55
+ /E2E\ <- Critical paths only (slowest)
56
+ /─────\
57
+ / Visual \ <- Screenshot comparisons
58
+ /───────────\
59
+ / Integration \ <- Service boundaries, APIs
60
+ /───────────────\
61
+ / Component \ <- UI components isolated
62
+ /───────────────────\
63
+ / Unit \ <- Fast, business logic (most)
64
+ /─────────────────────────\
65
+ ```
103
66
 
104
- // Dimension 3: Security
105
- interface SecurityTests {
106
- authentication: Test[]; // Auth works correctly
107
- authorization: Test[]; // Permissions enforced
108
- injection: Test[]; // SQL, XSS, etc. prevented
109
- dataProtection: Test[]; // Sensitive data secured
110
- }
67
+ ### Four Quality Dimensions
111
68
 
112
- // Dimension 4: Accessibility
113
- interface AccessibilityTests {
114
- screenReader: Test[]; // Works with assistive tech
115
- keyboard: Test[]; // Keyboard navigation works
116
- contrast: Test[]; // Visual contrast sufficient
117
- motion: Test[]; // Respects motion preferences
69
+ ```typescript
70
+ interface OmegaTestSuite {
71
+ accuracy: {
72
+ happyPath: Test[]; // Normal use cases
73
+ edgeCases: Test[]; // Boundary conditions
74
+ errorCases: Test[]; // Failure handling
75
+ };
76
+ performance: {
77
+ responseTime: Test[]; // p50, p95, p99 latency
78
+ throughput: Test[]; // Requests per second
79
+ memory: Test[]; // Leak detection
80
+ };
81
+ security: {
82
+ authentication: Test[];
83
+ authorization: Test[];
84
+ injection: Test[]; // SQL, XSS prevention
85
+ };
86
+ accessibility: {
87
+ wcag: Test[]; // WCAG 2.1 AA
88
+ keyboard: Test[]; // Tab navigation
89
+ screenReader: Test[]; // ARIA labels
90
+ };
118
91
  }
119
92
  ```
120
93
 
121
- ### 3. Unit Testing Patterns
94
+ ### Unit Testing Patterns
122
95
 
123
96
  ```typescript
124
- /**
125
- * Unit Tests: Fast, isolated, focused on business logic
126
- */
127
-
128
- import { describe, it, expect, vi, beforeEach } from 'vitest';
129
-
130
- // Pattern 1: Arrange-Act-Assert (AAA)
97
+ // Arrange-Act-Assert pattern
131
98
  describe('calculateDiscount', () => {
132
99
  it('applies 10% discount for orders over $100', () => {
133
100
  // Arrange
134
101
  const order = createOrder({ subtotal: 150 });
135
- const discountRules = createDiscountRules();
136
102
 
137
103
  // Act
138
- const result = calculateDiscount(order, discountRules);
104
+ const result = calculateDiscount(order);
139
105
 
140
106
  // Assert
141
107
  expect(result.discount).toBe(15);
@@ -143,828 +109,174 @@ describe('calculateDiscount', () => {
143
109
  });
144
110
  });
145
111
 
146
- // Pattern 2: Testing Edge Cases Systematically
147
- describe('validateEmail', () => {
148
- // Happy path
149
- it('accepts valid email formats', () => {
150
- const validEmails = [
151
- 'user@example.com',
152
- 'user.name@example.co.uk',
153
- 'user+tag@example.org'
154
- ];
155
-
156
- validEmails.forEach(email => {
157
- expect(validateEmail(email)).toBe(true);
158
- });
159
- });
160
-
161
- // Edge cases
162
- it.each([
163
- ['missing @', 'userexample.com'],
164
- ['missing domain', 'user@'],
165
- ['missing local part', '@example.com'],
166
- ['invalid characters', 'user<script>@example.com'],
167
- ['empty string', ''],
168
- ['whitespace only', ' '],
169
- ])('rejects %s: %s', (_description, email) => {
170
- expect(validateEmail(email)).toBe(false);
171
- });
172
-
173
- // Boundary conditions
174
- it('handles maximum length email', () => {
175
- const longEmail = 'a'.repeat(64) + '@' + 'b'.repeat(63) + '.com';
176
- expect(validateEmail(longEmail)).toBe(true);
177
- });
178
-
179
- it('rejects email exceeding maximum length', () => {
180
- const tooLongEmail = 'a'.repeat(65) + '@' + 'b'.repeat(64) + '.com';
181
- expect(validateEmail(tooLongEmail)).toBe(false);
182
- });
183
- });
184
-
185
- // Pattern 3: Testing Error Handling
186
- describe('fetchUserData', () => {
187
- const mockApi = vi.fn();
188
-
189
- beforeEach(() => {
190
- mockApi.mockReset();
191
- });
192
-
193
- it('throws UserNotFoundError when user does not exist', async () => {
194
- mockApi.mockRejectedValue(new Error('404'));
195
-
196
- await expect(fetchUserData('nonexistent-id', mockApi))
197
- .rejects
198
- .toThrow(UserNotFoundError);
199
- });
200
-
201
- it('retries on transient failures', async () => {
202
- mockApi
203
- .mockRejectedValueOnce(new Error('timeout'))
204
- .mockRejectedValueOnce(new Error('timeout'))
205
- .mockResolvedValueOnce({ id: '123', name: 'Test' });
206
-
207
- const result = await fetchUserData('123', mockApi);
208
-
209
- expect(mockApi).toHaveBeenCalledTimes(3);
210
- expect(result.name).toBe('Test');
211
- });
212
-
213
- it('gives up after max retries', async () => {
214
- mockApi.mockRejectedValue(new Error('timeout'));
215
-
216
- await expect(fetchUserData('123', mockApi))
217
- .rejects
218
- .toThrow(MaxRetriesExceededError);
219
-
220
- expect(mockApi).toHaveBeenCalledTimes(3);
221
- });
112
+ // Parameterized edge cases
113
+ it.each([
114
+ ['missing @', 'userexample.com', false],
115
+ ['valid format', 'user@example.com', true],
116
+ ['empty string', '', false],
117
+ ])('validateEmail %s: %s -> %s', (_desc, email, expected) => {
118
+ expect(validateEmail(email)).toBe(expected);
222
119
  });
223
120
 
224
- // Pattern 4: Testing Pure Functions with Properties
225
- describe('sortUsers (property-based)', () => {
226
- it('output length equals input length', () => {
227
- fc.assert(
228
- fc.property(fc.array(fc.record({
229
- id: fc.string(),
230
- name: fc.string(),
231
- age: fc.nat()
232
- })), (users) => {
233
- const sorted = sortUsers(users, 'name');
234
- return sorted.length === users.length;
235
- })
236
- );
237
- });
238
-
239
- it('maintains all original elements', () => {
240
- fc.assert(
241
- fc.property(fc.array(userArbitrary), (users) => {
242
- const sorted = sortUsers(users, 'name');
243
- const originalIds = new Set(users.map(u => u.id));
244
- const sortedIds = new Set(sorted.map(u => u.id));
245
- return setsEqual(originalIds, sortedIds);
246
- })
247
- );
248
- });
249
-
250
- it('result is actually sorted', () => {
251
- fc.assert(
252
- fc.property(fc.array(userArbitrary), (users) => {
253
- const sorted = sortUsers(users, 'name');
254
- return sorted.every((user, i) =>
255
- i === 0 || user.name >= sorted[i - 1].name
256
- );
257
- })
258
- );
259
- });
121
+ // Property-based testing
122
+ it('sorted array has same length as input', () => {
123
+ fc.assert(fc.property(fc.array(fc.nat()), (arr) => {
124
+ return sortArray(arr).length === arr.length;
125
+ }));
260
126
  });
261
127
  ```
262
128
 
263
- ### 4. Integration Testing Patterns
129
+ ### Integration Testing
264
130
 
265
131
  ```typescript
266
- /**
267
- * Integration Tests: Test service boundaries and contracts
268
- */
269
-
270
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
271
- import { createTestDatabase, cleanupTestDatabase } from './test-utils';
272
-
273
132
  describe('UserService Integration', () => {
274
133
  let db: TestDatabase;
275
- let userService: UserService;
276
-
277
- beforeAll(async () => {
278
- db = await createTestDatabase();
279
- userService = new UserService(db);
280
- });
281
-
282
- afterAll(async () => {
283
- await cleanupTestDatabase(db);
284
- });
285
-
286
- // Test real database interactions
287
- describe('createUser', () => {
288
- it('persists user to database', async () => {
289
- const userData = {
290
- email: 'test@example.com',
291
- name: 'Test User'
292
- };
293
-
294
- const user = await userService.createUser(userData);
295
-
296
- // Verify in database directly
297
- const dbUser = await db.query('SELECT * FROM users WHERE id = $1', [user.id]);
298
- expect(dbUser.email).toBe(userData.email);
299
- expect(dbUser.name).toBe(userData.name);
300
- });
301
-
302
- it('enforces unique email constraint', async () => {
303
- const userData = { email: 'duplicate@example.com', name: 'User 1' };
304
- await userService.createUser(userData);
305
-
306
- await expect(userService.createUser({
307
- email: 'duplicate@example.com',
308
- name: 'User 2'
309
- })).rejects.toThrow(DuplicateEmailError);
310
- });
311
- });
312
-
313
- // Test transactions
314
- describe('transferCredits', () => {
315
- it('atomically transfers credits between users', async () => {
316
- const sender = await userService.createUser({ credits: 100 });
317
- const receiver = await userService.createUser({ credits: 0 });
318
-
319
- await userService.transferCredits(sender.id, receiver.id, 50);
320
-
321
- const [updatedSender, updatedReceiver] = await Promise.all([
322
- userService.getUser(sender.id),
323
- userService.getUser(receiver.id)
324
- ]);
325
-
326
- expect(updatedSender.credits).toBe(50);
327
- expect(updatedReceiver.credits).toBe(50);
328
- });
329
134
 
330
- it('rolls back on failure', async () => {
331
- const sender = await userService.createUser({ credits: 100 });
332
- const receiver = await userService.createUser({ credits: 0 });
135
+ beforeAll(async () => { db = await createTestDatabase(); });
136
+ afterAll(async () => { await cleanupTestDatabase(db); });
333
137
 
334
- // Simulate failure mid-transaction
335
- vi.spyOn(db, 'commit').mockRejectedValueOnce(new Error('DB error'));
336
-
337
- await expect(
338
- userService.transferCredits(sender.id, receiver.id, 50)
339
- ).rejects.toThrow();
340
-
341
- // Verify no changes persisted
342
- const [s, r] = await Promise.all([
343
- userService.getUser(sender.id),
344
- userService.getUser(receiver.id)
345
- ]);
346
-
347
- expect(s.credits).toBe(100);
348
- expect(r.credits).toBe(0);
138
+ it('persists user to database', async () => {
139
+ const user = await userService.createUser({
140
+ email: 'test@example.com', name: 'Test'
349
141
  });
350
- });
351
- });
352
-
353
- // API Contract Testing
354
- describe('API Contract Tests', () => {
355
- it('GET /users/:id returns correct schema', async () => {
356
- const response = await api.get('/users/123');
357
-
358
- expect(response.status).toBe(200);
359
- expect(response.body).toMatchSchema({
360
- type: 'object',
361
- required: ['id', 'email', 'name', 'createdAt'],
362
- properties: {
363
- id: { type: 'string', format: 'uuid' },
364
- email: { type: 'string', format: 'email' },
365
- name: { type: 'string', minLength: 1 },
366
- createdAt: { type: 'string', format: 'date-time' }
367
- }
368
- });
369
- });
370
-
371
- it('POST /users validates request body', async () => {
372
- const response = await api.post('/users', {
373
- body: { email: 'invalid-email' } // Missing name, invalid email
374
- });
375
-
376
- expect(response.status).toBe(400);
377
- expect(response.body.errors).toContainEqual(
378
- expect.objectContaining({ field: 'email', message: expect.any(String) })
379
- );
380
- expect(response.body.errors).toContainEqual(
381
- expect.objectContaining({ field: 'name', message: expect.any(String) })
382
- );
383
- });
384
- });
385
- ```
386
-
387
- ### 5. E2E Testing Patterns
388
-
389
- ```typescript
390
- /**
391
- * E2E Tests: Test critical user journeys
392
- * Use sparingly - slow and expensive
393
- */
394
-
395
- import { test, expect } from '@playwright/test';
396
-
397
- // Critical Path: User Registration & Login
398
- test.describe('Authentication Flow', () => {
399
- test('new user can register and login', async ({ page }) => {
400
- const testEmail = `test-${Date.now()}@example.com`;
401
-
402
- // Registration
403
- await page.goto('/register');
404
- await page.fill('[data-testid="email"]', testEmail);
405
- await page.fill('[data-testid="password"]', 'SecurePass123!');
406
- await page.fill('[data-testid="confirm-password"]', 'SecurePass123!');
407
- await page.click('[data-testid="register-button"]');
408
-
409
- // Verify registration success
410
- await expect(page).toHaveURL('/verify-email');
411
- await expect(page.locator('[data-testid="success-message"]'))
412
- .toContainText('verification email sent');
413
-
414
- // Simulate email verification (in test environment)
415
- await verifyEmailInTestMode(testEmail);
416
-
417
- // Login
418
- await page.goto('/login');
419
- await page.fill('[data-testid="email"]', testEmail);
420
- await page.fill('[data-testid="password"]', 'SecurePass123!');
421
- await page.click('[data-testid="login-button"]');
422
-
423
- // Verify login success
424
- await expect(page).toHaveURL('/dashboard');
425
- await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
426
- });
427
-
428
- test('handles invalid credentials', async ({ page }) => {
429
- await page.goto('/login');
430
- await page.fill('[data-testid="email"]', 'wrong@example.com');
431
- await page.fill('[data-testid="password"]', 'wrongpassword');
432
- await page.click('[data-testid="login-button"]');
433
-
434
- await expect(page.locator('[data-testid="error-message"]'))
435
- .toContainText('Invalid email or password');
436
- await expect(page).toHaveURL('/login');
437
- });
438
- });
439
142
 
440
- // Critical Path: E-commerce Checkout
441
- test.describe('Checkout Flow', () => {
442
- test.beforeEach(async ({ page }) => {
443
- // Login as test user
444
- await loginAsTestUser(page);
143
+ const dbUser = await db.query('SELECT * FROM users WHERE id = $1', [user.id]);
144
+ expect(dbUser.email).toBe('test@example.com');
445
145
  });
446
146
 
447
- test('complete purchase flow', async ({ page }) => {
448
- // Add item to cart
449
- await page.goto('/products/test-product');
450
- await page.click('[data-testid="add-to-cart"]');
451
- await expect(page.locator('[data-testid="cart-count"]')).toHaveText('1');
452
-
453
- // Go to checkout
454
- await page.click('[data-testid="cart-icon"]');
455
- await page.click('[data-testid="checkout-button"]');
456
-
457
- // Fill shipping info
458
- await page.fill('[data-testid="address"]', '123 Test St');
459
- await page.fill('[data-testid="city"]', 'Test City');
460
- await page.fill('[data-testid="zip"]', '12345');
461
- await page.click('[data-testid="continue-to-payment"]');
462
-
463
- // Payment (use test card)
464
- await page.fill('[data-testid="card-number"]', '4242424242424242');
465
- await page.fill('[data-testid="expiry"]', '12/25');
466
- await page.fill('[data-testid="cvc"]', '123');
467
- await page.click('[data-testid="place-order"]');
468
-
469
- // Verify success
470
- await expect(page).toHaveURL(/\/orders\/[a-z0-9-]+/);
471
- await expect(page.locator('[data-testid="order-status"]'))
472
- .toHaveText('Order Confirmed');
473
- });
474
- });
147
+ it('rolls back transaction on failure', async () => {
148
+ vi.spyOn(db, 'commit').mockRejectedValueOnce(new Error('DB error'));
475
149
 
476
- // Visual Regression Testing
477
- test.describe('Visual Regression', () => {
478
- test('dashboard matches snapshot', async ({ page }) => {
479
- await loginAsTestUser(page);
480
- await page.goto('/dashboard');
481
- await page.waitForLoadState('networkidle');
150
+ await expect(userService.transfer(from, to, 50)).rejects.toThrow();
482
151
 
483
- await expect(page).toHaveScreenshot('dashboard.png', {
484
- maxDiffPixelRatio: 0.01
485
- });
486
- });
487
-
488
- test('responsive layout on mobile', async ({ page }) => {
489
- await page.setViewportSize({ width: 375, height: 667 });
490
- await page.goto('/');
491
-
492
- await expect(page).toHaveScreenshot('home-mobile.png');
152
+ // Verify no changes persisted
153
+ expect(await userService.getBalance(from)).toBe(originalBalance);
493
154
  });
494
155
  });
495
156
  ```
496
157
 
497
- ### 6. Performance Testing
158
+ ### Performance Testing
498
159
 
499
160
  ```typescript
500
- /**
501
- * Performance Tests: Ensure system meets performance requirements
502
- */
503
-
504
- import { describe, it, expect } from 'vitest';
505
-
506
- // Response Time Testing
507
161
  describe('API Performance', () => {
508
- it('GET /users responds within 100ms', async () => {
509
- const iterations = 100;
162
+ it('responds within SLA', async () => {
510
163
  const times: number[] = [];
511
-
512
- for (let i = 0; i < iterations; i++) {
164
+ for (let i = 0; i < 100; i++) {
513
165
  const start = performance.now();
514
166
  await api.get('/users');
515
167
  times.push(performance.now() - start);
516
168
  }
517
169
 
518
- const p50 = percentile(times, 50);
519
- const p95 = percentile(times, 95);
520
- const p99 = percentile(times, 99);
521
-
522
- expect(p50).toBeLessThan(50); // Median under 50ms
523
- expect(p95).toBeLessThan(100); // 95th percentile under 100ms
524
- expect(p99).toBeLessThan(200); // 99th percentile under 200ms
170
+ expect(percentile(times, 50)).toBeLessThan(50); // p50 < 50ms
171
+ expect(percentile(times, 95)).toBeLessThan(100); // p95 < 100ms
172
+ expect(percentile(times, 99)).toBeLessThan(200); // p99 < 200ms
525
173
  });
526
174
 
527
- it('handles concurrent requests', async () => {
528
- const concurrency = 50;
529
- const requests = Array(concurrency).fill(null).map(() =>
530
- api.get('/users')
531
- );
532
-
533
- const start = performance.now();
175
+ it('handles concurrent load', async () => {
176
+ const requests = Array(50).fill(null).map(() => api.get('/users'));
534
177
  const responses = await Promise.all(requests);
535
- const duration = performance.now() - start;
536
178
 
537
- // All should succeed
538
179
  expect(responses.every(r => r.status === 200)).toBe(true);
539
- // Should complete in reasonable time
540
- expect(duration).toBeLessThan(1000);
541
180
  });
542
181
  });
543
-
544
- // Memory Leak Detection
545
- describe('Memory Stability', () => {
546
- it('does not leak memory over many operations', async () => {
547
- const initialMemory = process.memoryUsage().heapUsed;
548
-
549
- // Perform many operations
550
- for (let i = 0; i < 10000; i++) {
551
- await processData(generateLargePayload());
552
- }
553
-
554
- // Force garbage collection if available
555
- if (global.gc) global.gc();
556
-
557
- const finalMemory = process.memoryUsage().heapUsed;
558
- const growth = finalMemory - initialMemory;
559
-
560
- // Memory should not grow significantly (< 10MB)
561
- expect(growth).toBeLessThan(10 * 1024 * 1024);
562
- });
563
- });
564
-
565
- // Load Testing Configuration
566
- const loadTestConfig = {
567
- scenarios: {
568
- normalLoad: {
569
- executor: 'ramping-vus',
570
- startVUs: 0,
571
- stages: [
572
- { duration: '2m', target: 100 }, // Ramp up
573
- { duration: '5m', target: 100 }, // Steady state
574
- { duration: '2m', target: 0 } // Ramp down
575
- ],
576
- gracefulRampDown: '30s'
577
- },
578
-
579
- stressTest: {
580
- executor: 'ramping-vus',
581
- startVUs: 0,
582
- stages: [
583
- { duration: '2m', target: 200 },
584
- { duration: '5m', target: 200 },
585
- { duration: '2m', target: 400 }, // Push beyond normal
586
- { duration: '5m', target: 400 },
587
- { duration: '2m', target: 0 }
588
- ]
589
- },
590
-
591
- spikeTest: {
592
- executor: 'ramping-vus',
593
- startVUs: 0,
594
- stages: [
595
- { duration: '10s', target: 500 }, // Sudden spike
596
- { duration: '1m', target: 500 },
597
- { duration: '10s', target: 0 }
598
- ]
599
- }
600
- },
601
-
602
- thresholds: {
603
- http_req_duration: ['p(95)<200', 'p(99)<500'],
604
- http_req_failed: ['rate<0.01'],
605
- http_reqs: ['rate>100']
606
- }
607
- };
608
182
  ```
609
183
 
610
- ### 7. Security Testing
184
+ ### Security Testing
611
185
 
612
186
  ```typescript
613
- /**
614
- * Security Tests: Verify protection against common attacks
615
- */
616
-
617
187
  describe('Security Tests', () => {
618
- // SQL Injection Prevention
619
- describe('SQL Injection', () => {
620
- const sqlInjectionPayloads = [
621
- "'; DROP TABLE users; --",
622
- "' OR '1'='1",
623
- "'; INSERT INTO users VALUES ('hacker', 'hacked'); --",
624
- "1; UPDATE users SET role='admin' WHERE id=1; --"
625
- ];
626
-
627
- it.each(sqlInjectionPayloads)(
628
- 'safely handles SQL injection attempt: %s',
629
- async (payload) => {
630
- const response = await api.get(`/users?search=${encodeURIComponent(payload)}`);
631
-
632
- // Should not error (indicates parameterized queries)
633
- expect(response.status).not.toBe(500);
634
-
635
- // Verify database integrity
636
- const users = await db.query('SELECT * FROM users');
637
- expect(users).toBeDefined();
638
- }
639
- );
640
- });
188
+ const sqlPayloads = ["'; DROP TABLE users; --", "' OR '1'='1"];
189
+ const xssPayloads = ['<script>alert("xss")</script>', '<img onerror=alert(1)>'];
641
190
 
642
- // XSS Prevention
643
- describe('XSS Prevention', () => {
644
- const xssPayloads = [
645
- '<script>alert("xss")</script>',
646
- '<img src=x onerror=alert("xss")>',
647
- '"><script>alert(document.cookie)</script>',
648
- "javascript:alert('xss')"
649
- ];
650
-
651
- it.each(xssPayloads)(
652
- 'escapes XSS payload: %s',
653
- async (payload) => {
654
- // Create content with XSS payload
655
- await api.post('/posts', { body: { content: payload } });
656
-
657
- // Retrieve and verify it's escaped
658
- const response = await api.get('/posts');
659
- const html = response.body.posts[0].content;
660
-
661
- expect(html).not.toContain('<script>');
662
- expect(html).not.toContain('onerror=');
663
- expect(html).not.toContain('javascript:');
664
- }
665
- );
191
+ it.each(sqlPayloads)('prevents SQL injection: %s', async (payload) => {
192
+ const response = await api.get(`/users?search=${encodeURIComponent(payload)}`);
193
+ expect(response.status).not.toBe(500);
194
+ expect(await db.query('SELECT * FROM users')).toBeDefined();
666
195
  });
667
196
 
668
- // Authentication Security
669
- describe('Authentication', () => {
670
- it('rate limits login attempts', async () => {
671
- const attempts = 10;
672
- const responses: Response[] = [];
673
-
674
- for (let i = 0; i < attempts; i++) {
675
- responses.push(await api.post('/login', {
676
- body: { email: 'test@example.com', password: 'wrong' }
677
- }));
678
- }
679
-
680
- // Later attempts should be rate limited
681
- const rateLimited = responses.filter(r => r.status === 429);
682
- expect(rateLimited.length).toBeGreaterThan(0);
683
- });
684
-
685
- it('uses secure session cookies', async () => {
686
- const response = await api.post('/login', {
687
- body: { email: 'test@example.com', password: 'correct' }
688
- });
689
-
690
- const sessionCookie = response.headers['set-cookie'];
691
- expect(sessionCookie).toContain('HttpOnly');
692
- expect(sessionCookie).toContain('Secure');
693
- expect(sessionCookie).toContain('SameSite');
694
- });
695
-
696
- it('invalidates session on logout', async () => {
697
- const loginResponse = await api.post('/login', {
698
- body: { email: 'test@example.com', password: 'correct' }
699
- });
700
- const sessionToken = extractSessionToken(loginResponse);
701
-
702
- await api.post('/logout', { headers: { Cookie: sessionToken } });
703
-
704
- // Old session should be invalid
705
- const response = await api.get('/me', {
706
- headers: { Cookie: sessionToken }
707
- });
708
- expect(response.status).toBe(401);
709
- });
197
+ it.each(xssPayloads)('escapes XSS payload: %s', async (payload) => {
198
+ await api.post('/posts', { content: payload });
199
+ const html = (await api.get('/posts')).body.posts[0].content;
200
+ expect(html).not.toContain('<script>');
710
201
  });
711
202
 
712
- // Authorization Testing
713
- describe('Authorization', () => {
714
- it('prevents accessing other users data', async () => {
715
- const user1Token = await loginAs('user1');
716
- const user2Token = await loginAs('user2');
717
-
718
- // User 1 tries to access User 2's data
719
- const response = await api.get('/users/user2/private-data', {
720
- headers: { Authorization: `Bearer ${user1Token}` }
721
- });
722
-
723
- expect(response.status).toBe(403);
724
- });
725
-
726
- it('admin routes require admin role', async () => {
727
- const userToken = await loginAs('regular-user');
728
-
729
- const response = await api.get('/admin/users', {
730
- headers: { Authorization: `Bearer ${userToken}` }
731
- });
732
-
733
- expect(response.status).toBe(403);
734
- });
203
+ it('rate limits login attempts', async () => {
204
+ for (let i = 0; i < 10; i++) {
205
+ await api.post('/login', { email: 'x', password: 'wrong' });
206
+ }
207
+ const response = await api.post('/login', { email: 'x', password: 'wrong' });
208
+ expect(response.status).toBe(429);
735
209
  });
736
210
  });
737
211
  ```
738
212
 
739
- ### 8. Accessibility Testing
213
+ ### Accessibility Testing
740
214
 
741
215
  ```typescript
742
- /**
743
- * Accessibility Tests: Ensure usability for all users
744
- */
745
-
746
- import { test, expect } from '@playwright/test';
747
- import AxeBuilder from '@axe-core/playwright';
748
-
749
216
  test.describe('Accessibility', () => {
750
- // Automated WCAG Compliance
751
- test('home page has no accessibility violations', async ({ page }) => {
217
+ test('page has no WCAG violations', async ({ page }) => {
752
218
  await page.goto('/');
753
-
754
219
  const results = await new AxeBuilder({ page })
755
220
  .withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
756
221
  .analyze();
757
-
758
222
  expect(results.violations).toEqual([]);
759
223
  });
760
224
 
761
- // Keyboard Navigation
762
- test('all interactive elements are keyboard accessible', async ({ page }) => {
225
+ test('keyboard navigation works', async ({ page }) => {
763
226
  await page.goto('/');
227
+ const focusable = 'a, button, input, [tabindex]:not([tabindex="-1"])';
228
+ const elements = await page.locator(focusable).all();
764
229
 
765
- // Tab through all focusable elements
766
- const focusableSelectors = 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
767
- const elements = await page.locator(focusableSelectors).all();
768
-
769
- for (const element of elements) {
230
+ for (const _ of elements) {
770
231
  await page.keyboard.press('Tab');
771
232
  const focused = await page.evaluate(() => document.activeElement?.tagName);
772
233
  expect(focused).toBeDefined();
773
234
  }
774
235
  });
775
236
 
776
- // Focus Management
777
- test('modal traps focus correctly', async ({ page }) => {
778
- await page.goto('/');
779
- await page.click('[data-testid="open-modal"]');
780
-
781
- // Focus should be on modal
782
- const modalFocused = await page.locator('[data-testid="modal"]').evaluate(
783
- el => el.contains(document.activeElement)
784
- );
785
- expect(modalFocused).toBe(true);
786
-
787
- // Tab should stay within modal
788
- for (let i = 0; i < 10; i++) {
789
- await page.keyboard.press('Tab');
790
- const stillInModal = await page.locator('[data-testid="modal"]').evaluate(
791
- el => el.contains(document.activeElement)
792
- );
793
- expect(stillInModal).toBe(true);
794
- }
795
-
796
- // Escape closes modal
797
- await page.keyboard.press('Escape');
798
- await expect(page.locator('[data-testid="modal"]')).not.toBeVisible();
799
- });
800
-
801
- // Screen Reader Compatibility
802
237
  test('images have alt text', async ({ page }) => {
803
- await page.goto('/');
804
-
805
238
  const images = await page.locator('img').all();
806
239
  for (const img of images) {
807
- const alt = await img.getAttribute('alt');
808
- expect(alt).toBeDefined();
809
- expect(alt?.length).toBeGreaterThan(0);
240
+ expect(await img.getAttribute('alt')).toBeTruthy();
810
241
  }
811
242
  });
812
-
813
- // Color Contrast
814
- test('text has sufficient contrast', async ({ page }) => {
815
- await page.goto('/');
816
-
817
- const results = await new AxeBuilder({ page })
818
- .withRules(['color-contrast'])
819
- .analyze();
820
-
821
- expect(results.violations).toEqual([]);
822
- });
823
-
824
- // Reduced Motion
825
- test('respects prefers-reduced-motion', async ({ page }) => {
826
- await page.emulateMedia({ reducedMotion: 'reduce' });
827
- await page.goto('/');
828
-
829
- // Check animations are disabled
830
- const animatedElement = page.locator('[data-animated]');
831
- const animationDuration = await animatedElement.evaluate(el =>
832
- getComputedStyle(el).animationDuration
833
- );
834
-
835
- expect(animationDuration).toBe('0s');
836
- });
837
243
  });
838
244
  ```
839
245
 
840
- ## Use Cases
841
-
842
- ### Testing a New Feature
246
+ ### E2E Critical Path
843
247
 
844
248
  ```typescript
845
- /**
846
- * Complete test suite for a new user profile feature
847
- */
848
-
849
- // Unit Tests
850
- describe('ProfileService', () => {
851
- it('validates profile data', () => {
852
- expect(validateProfile({ name: '' })).toEqual({
853
- valid: false,
854
- errors: ['Name is required']
855
- });
856
- });
857
-
858
- it('sanitizes bio field', () => {
859
- const result = sanitizeProfile({
860
- bio: '<script>alert("xss")</script>Hello'
861
- });
862
- expect(result.bio).toBe('Hello');
863
- });
864
- });
865
-
866
- // Integration Tests
867
- describe('Profile API', () => {
868
- it('updates profile in database', async () => {
869
- const response = await api.put('/profile', {
870
- body: { name: 'New Name', bio: 'New bio' }
871
- });
872
-
873
- expect(response.status).toBe(200);
874
-
875
- const dbProfile = await db.profiles.findById(userId);
876
- expect(dbProfile.name).toBe('New Name');
877
- });
878
- });
879
-
880
- // E2E Tests
881
- test('user can update their profile', async ({ page }) => {
249
+ test('complete purchase flow', async ({ page }) => {
250
+ // Login
882
251
  await loginAsTestUser(page);
883
- await page.goto('/settings/profile');
884
- await page.fill('[data-testid="name"]', 'Updated Name');
885
- await page.click('[data-testid="save"]');
886
252
 
887
- await expect(page.locator('[data-testid="success"]')).toBeVisible();
888
- });
889
- ```
253
+ // Add to cart
254
+ await page.goto('/products/test-product');
255
+ await page.click('[data-testid="add-to-cart"]');
890
256
 
891
- ### CI/CD Test Configuration
257
+ // Checkout
258
+ await page.click('[data-testid="checkout-button"]');
259
+ await page.fill('[data-testid="card-number"]', '4242424242424242');
260
+ await page.click('[data-testid="place-order"]');
892
261
 
893
- ```yaml
894
- # .github/workflows/test.yml
895
- name: Test Suite
896
-
897
- on: [push, pull_request]
898
-
899
- jobs:
900
- unit-tests:
901
- runs-on: ubuntu-latest
902
- steps:
903
- - uses: actions/checkout@v4
904
- - name: Run unit tests
905
- run: npm run test:unit -- --coverage
906
- - name: Upload coverage
907
- uses: codecov/codecov-action@v4
908
-
909
- integration-tests:
910
- runs-on: ubuntu-latest
911
- services:
912
- postgres:
913
- image: postgres:15
914
- env:
915
- POSTGRES_PASSWORD: test
916
- steps:
917
- - uses: actions/checkout@v4
918
- - name: Run integration tests
919
- run: npm run test:integration
920
-
921
- e2e-tests:
922
- runs-on: ubuntu-latest
923
- steps:
924
- - uses: actions/checkout@v4
925
- - name: Install Playwright
926
- run: npx playwright install --with-deps
927
- - name: Run E2E tests
928
- run: npm run test:e2e
929
- - uses: actions/upload-artifact@v4
930
- if: failure()
931
- with:
932
- name: playwright-report
933
- path: playwright-report/
262
+ // Verify
263
+ await expect(page).toHaveURL(/\/orders\/[a-z0-9-]+/);
264
+ await expect(page.locator('[data-testid="order-status"]'))
265
+ .toHaveText('Order Confirmed');
266
+ });
934
267
  ```
935
268
 
936
269
  ## Best Practices
937
270
 
938
- ### Do's
939
-
940
- - **Test all four quality dimensions** (accuracy, performance, security, accessibility)
941
- - **Follow the test pyramid** - more unit tests, fewer E2E tests
942
- - **Use descriptive test names** that explain the expected behavior
943
- - **Test edge cases and error conditions** systematically
944
- - **Keep tests independent** - no shared state between tests
945
- - **Use appropriate assertions** that give helpful error messages
946
- - **Mock external dependencies** in unit tests
947
- - **Test real integrations** in integration tests
948
- - **Run tests in CI/CD** on every commit
949
- - **Maintain test data factories** for consistent test data
950
-
951
- ### Don'ts
952
-
953
- - Don't test implementation details - test behavior
954
- - Don't write flaky tests - fix or delete them
955
- - Don't skip tests without documented reason
956
- - Don't test framework code - trust your dependencies
957
- - Don't use sleep/delays - use proper async handling
958
- - Don't hardcode test data - use factories
959
- - Don't ignore failing tests - fix them immediately
960
- - Don't over-mock - some integration is valuable
961
- - Don't write tests after bugs escape - prevent them
962
- - Don't chase 100% coverage - chase meaningful coverage
963
-
964
- ## References
965
-
966
- - [Testing Library](https://testing-library.com/)
967
- - [Vitest Documentation](https://vitest.dev/)
968
- - [Playwright Documentation](https://playwright.dev/)
969
- - [OWASP Testing Guide](https://owasp.org/www-project-web-security-testing-guide/)
970
- - [Web Content Accessibility Guidelines](https://www.w3.org/WAI/standards-guidelines/wcag/)
271
+ | Do | Avoid |
272
+ |----|-------|
273
+ | Test all four quality dimensions | Testing only happy paths |
274
+ | Follow the test pyramid (more units) | Relying heavily on E2E tests |
275
+ | Use descriptive test names | Testing implementation details |
276
+ | Test edge cases systematically | Writing flaky tests |
277
+ | Keep tests independent (no shared state) | Using sleep/delays for timing |
278
+ | Use factories for test data | Hardcoding test data |
279
+ | Mock external dependencies in unit tests | Over-mocking in integration tests |
280
+ | Run tests in CI on every commit | Ignoring failing tests |
281
+ | Fix flaky tests immediately | Skipping tests without reason |
282
+ | Chase meaningful coverage, not 100% | Testing framework code |