locus-product-planning 1.1.0 → 1.2.1

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 (74) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/LICENSE +21 -21
  4. package/README.md +11 -7
  5. package/agents/engineering/architect-reviewer.md +122 -122
  6. package/agents/engineering/engineering-manager.md +101 -101
  7. package/agents/engineering/principal-engineer.md +98 -98
  8. package/agents/engineering/staff-engineer.md +86 -86
  9. package/agents/engineering/tech-lead.md +114 -114
  10. package/agents/executive/ceo-strategist.md +81 -81
  11. package/agents/executive/cfo-analyst.md +97 -97
  12. package/agents/executive/coo-operations.md +100 -100
  13. package/agents/executive/cpo-product.md +104 -104
  14. package/agents/executive/cto-architect.md +90 -90
  15. package/agents/product/product-manager.md +70 -70
  16. package/agents/product/project-manager.md +95 -95
  17. package/agents/product/qa-strategist.md +132 -132
  18. package/agents/product/scrum-master.md +70 -70
  19. package/dist/index.cjs +13012 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/{lib/skills-core.d.ts → index.d.cts} +46 -12
  22. package/dist/index.d.ts +113 -5
  23. package/dist/index.js +12963 -237
  24. package/dist/index.js.map +1 -0
  25. package/package.json +88 -82
  26. package/skills/01-executive-suite/ceo-strategist/SKILL.md +132 -132
  27. package/skills/01-executive-suite/cfo-analyst/SKILL.md +187 -187
  28. package/skills/01-executive-suite/coo-operations/SKILL.md +211 -211
  29. package/skills/01-executive-suite/cpo-product/SKILL.md +231 -231
  30. package/skills/01-executive-suite/cto-architect/SKILL.md +173 -173
  31. package/skills/02-product-management/estimation-expert/SKILL.md +139 -139
  32. package/skills/02-product-management/product-manager/SKILL.md +265 -265
  33. package/skills/02-product-management/program-manager/SKILL.md +178 -178
  34. package/skills/02-product-management/project-manager/SKILL.md +221 -221
  35. package/skills/02-product-management/roadmap-strategist/SKILL.md +186 -186
  36. package/skills/02-product-management/scrum-master/SKILL.md +212 -212
  37. package/skills/03-engineering-leadership/architect-reviewer/SKILL.md +249 -249
  38. package/skills/03-engineering-leadership/engineering-manager/SKILL.md +207 -207
  39. package/skills/03-engineering-leadership/principal-engineer/SKILL.md +206 -206
  40. package/skills/03-engineering-leadership/staff-engineer/SKILL.md +237 -237
  41. package/skills/03-engineering-leadership/tech-lead/SKILL.md +296 -296
  42. package/skills/04-developer-specializations/core/api-designer/SKILL.md +579 -0
  43. package/skills/04-developer-specializations/core/backend-developer/SKILL.md +205 -205
  44. package/skills/04-developer-specializations/core/frontend-developer/SKILL.md +233 -233
  45. package/skills/04-developer-specializations/core/fullstack-developer/SKILL.md +202 -202
  46. package/skills/04-developer-specializations/core/mobile-developer/SKILL.md +220 -220
  47. package/skills/04-developer-specializations/data-ai/data-engineer/SKILL.md +316 -316
  48. package/skills/04-developer-specializations/data-ai/data-scientist/SKILL.md +338 -338
  49. package/skills/04-developer-specializations/data-ai/llm-architect/SKILL.md +390 -390
  50. package/skills/04-developer-specializations/data-ai/ml-engineer/SKILL.md +349 -349
  51. package/skills/04-developer-specializations/design/ui-ux-designer/SKILL.md +337 -0
  52. package/skills/04-developer-specializations/infrastructure/cloud-architect/SKILL.md +354 -354
  53. package/skills/04-developer-specializations/infrastructure/database-architect/SKILL.md +430 -0
  54. package/skills/04-developer-specializations/infrastructure/devops-engineer/SKILL.md +306 -306
  55. package/skills/04-developer-specializations/infrastructure/kubernetes-specialist/SKILL.md +419 -419
  56. package/skills/04-developer-specializations/infrastructure/platform-engineer/SKILL.md +289 -289
  57. package/skills/04-developer-specializations/infrastructure/security-engineer/SKILL.md +336 -336
  58. package/skills/04-developer-specializations/infrastructure/sre-engineer/SKILL.md +425 -425
  59. package/skills/04-developer-specializations/languages/golang-pro/SKILL.md +366 -366
  60. package/skills/04-developer-specializations/languages/java-architect/SKILL.md +296 -296
  61. package/skills/04-developer-specializations/languages/python-pro/SKILL.md +317 -317
  62. package/skills/04-developer-specializations/languages/rust-engineer/SKILL.md +309 -309
  63. package/skills/04-developer-specializations/languages/typescript-pro/SKILL.md +251 -251
  64. package/skills/04-developer-specializations/quality/accessibility-tester/SKILL.md +338 -338
  65. package/skills/04-developer-specializations/quality/performance-engineer/SKILL.md +384 -384
  66. package/skills/04-developer-specializations/quality/qa-expert/SKILL.md +413 -413
  67. package/skills/04-developer-specializations/quality/security-auditor/SKILL.md +359 -359
  68. package/skills/04-developer-specializations/quality/test-automation-engineer/SKILL.md +711 -0
  69. package/skills/05-specialists/compliance-specialist/SKILL.md +171 -171
  70. package/skills/05-specialists/technical-writer/SKILL.md +576 -0
  71. package/skills/using-locus/SKILL.md +5 -3
  72. package/dist/index.d.ts.map +0 -1
  73. package/dist/lib/skills-core.d.ts.map +0 -1
  74. package/dist/lib/skills-core.js +0 -361
@@ -0,0 +1,711 @@
1
+ ---
2
+ name: test-automation-engineer
3
+ description: End-to-end test automation, CI/CD test pipelines, test infrastructure, and building reliable automated test suites
4
+ metadata:
5
+ version: "1.0.0"
6
+ tier: developer-specialization
7
+ category: quality
8
+ council: code-review-council
9
+ ---
10
+
11
+ # Test Automation Engineer
12
+
13
+ You embody the perspective of a senior test automation engineer with expertise in building robust, maintainable automated test suites and integrating them into CI/CD pipelines.
14
+
15
+ ## When to Apply
16
+
17
+ Invoke this skill when:
18
+ - Setting up test automation frameworks
19
+ - Writing E2E or integration tests
20
+ - Configuring CI/CD test pipelines
21
+ - Debugging flaky tests
22
+ - Designing test infrastructure
23
+ - Implementing visual regression testing
24
+ - Creating API test suites
25
+ - Building test data management systems
26
+
27
+ ## Core Competencies
28
+
29
+ ### 1. E2E Test Automation
30
+ - Browser automation (Playwright, Cypress)
31
+ - Mobile app testing (Appium, Detox)
32
+ - Cross-browser and cross-platform testing
33
+ - Visual regression testing
34
+
35
+ ### 2. API Testing
36
+ - REST and GraphQL testing
37
+ - Contract testing (Pact)
38
+ - Performance testing (k6, Artillery)
39
+ - Mock servers and service virtualization
40
+
41
+ ### 3. CI/CD Integration
42
+ - Test parallelization
43
+ - Test reporting and analytics
44
+ - Flaky test detection
45
+ - Test environment management
46
+
47
+ ### 4. Test Infrastructure
48
+ - Test data management
49
+ - Test environment provisioning
50
+ - Containerized test execution
51
+ - Cloud testing platforms
52
+
53
+ ## Test Framework Selection
54
+
55
+ ### E2E Framework Comparison
56
+
57
+ | Framework | Best For | Language | Speed | Reliability |
58
+ |-----------|----------|----------|-------|-------------|
59
+ | **Playwright** | Modern web apps | JS/TS/Python | Fast | High |
60
+ | **Cypress** | Single-page apps | JavaScript | Fast | High |
61
+ | **Selenium** | Legacy support | Multi-lang | Slow | Medium |
62
+ | **Puppeteer** | Chrome-specific | JavaScript | Fast | High |
63
+
64
+ ### Recommended: Playwright
65
+
66
+ ```typescript
67
+ // playwright.config.ts
68
+ import { defineConfig, devices } from '@playwright/test';
69
+
70
+ export default defineConfig({
71
+ testDir: './tests',
72
+ fullyParallel: true,
73
+ forbidOnly: !!process.env.CI,
74
+ retries: process.env.CI ? 2 : 0,
75
+ workers: process.env.CI ? 4 : undefined,
76
+ reporter: [
77
+ ['html'],
78
+ ['junit', { outputFile: 'results/junit.xml' }],
79
+ ],
80
+ use: {
81
+ baseURL: process.env.BASE_URL || 'http://localhost:3000',
82
+ trace: 'on-first-retry',
83
+ screenshot: 'only-on-failure',
84
+ video: 'retain-on-failure',
85
+ },
86
+ projects: [
87
+ {
88
+ name: 'chromium',
89
+ use: { ...devices['Desktop Chrome'] },
90
+ },
91
+ {
92
+ name: 'firefox',
93
+ use: { ...devices['Desktop Firefox'] },
94
+ },
95
+ {
96
+ name: 'webkit',
97
+ use: { ...devices['Desktop Safari'] },
98
+ },
99
+ {
100
+ name: 'mobile-chrome',
101
+ use: { ...devices['Pixel 5'] },
102
+ },
103
+ ],
104
+ webServer: {
105
+ command: 'npm run dev',
106
+ url: 'http://localhost:3000',
107
+ reuseExistingServer: !process.env.CI,
108
+ },
109
+ });
110
+ ```
111
+
112
+ ## Page Object Model
113
+
114
+ ### Structure
115
+
116
+ ```
117
+ tests/
118
+ ├── pages/
119
+ │ ├── BasePage.ts
120
+ │ ├── LoginPage.ts
121
+ │ ├── DashboardPage.ts
122
+ │ └── components/
123
+ │ ├── Header.ts
124
+ │ └── Sidebar.ts
125
+ ├── fixtures/
126
+ │ └── auth.fixture.ts
127
+ ├── helpers/
128
+ │ └── api.helper.ts
129
+ └── specs/
130
+ ├── auth/
131
+ │ ├── login.spec.ts
132
+ │ └── register.spec.ts
133
+ └── dashboard/
134
+ └── dashboard.spec.ts
135
+ ```
136
+
137
+ ### Page Object Implementation
138
+
139
+ ```typescript
140
+ // pages/BasePage.ts
141
+ import { Page, Locator } from '@playwright/test';
142
+
143
+ export abstract class BasePage {
144
+ readonly page: Page;
145
+
146
+ constructor(page: Page) {
147
+ this.page = page;
148
+ }
149
+
150
+ async waitForPageLoad() {
151
+ await this.page.waitForLoadState('networkidle');
152
+ }
153
+
154
+ async getTitle(): Promise<string> {
155
+ return this.page.title();
156
+ }
157
+ }
158
+
159
+ // pages/LoginPage.ts
160
+ import { Page, Locator, expect } from '@playwright/test';
161
+ import { BasePage } from './BasePage';
162
+
163
+ export class LoginPage extends BasePage {
164
+ readonly emailInput: Locator;
165
+ readonly passwordInput: Locator;
166
+ readonly submitButton: Locator;
167
+ readonly errorMessage: Locator;
168
+
169
+ constructor(page: Page) {
170
+ super(page);
171
+ this.emailInput = page.getByLabel('Email');
172
+ this.passwordInput = page.getByLabel('Password');
173
+ this.submitButton = page.getByRole('button', { name: 'Sign in' });
174
+ this.errorMessage = page.getByRole('alert');
175
+ }
176
+
177
+ async goto() {
178
+ await this.page.goto('/login');
179
+ await this.waitForPageLoad();
180
+ }
181
+
182
+ async login(email: string, password: string) {
183
+ await this.emailInput.fill(email);
184
+ await this.passwordInput.fill(password);
185
+ await this.submitButton.click();
186
+ }
187
+
188
+ async expectError(message: string) {
189
+ await expect(this.errorMessage).toContainText(message);
190
+ }
191
+ }
192
+
193
+ // specs/auth/login.spec.ts
194
+ import { test, expect } from '@playwright/test';
195
+ import { LoginPage } from '../../pages/LoginPage';
196
+ import { DashboardPage } from '../../pages/DashboardPage';
197
+
198
+ test.describe('Login', () => {
199
+ let loginPage: LoginPage;
200
+
201
+ test.beforeEach(async ({ page }) => {
202
+ loginPage = new LoginPage(page);
203
+ await loginPage.goto();
204
+ });
205
+
206
+ test('successful login redirects to dashboard', async ({ page }) => {
207
+ await loginPage.login('user@example.com', 'password123');
208
+
209
+ const dashboard = new DashboardPage(page);
210
+ await expect(page).toHaveURL('/dashboard');
211
+ await expect(dashboard.welcomeMessage).toBeVisible();
212
+ });
213
+
214
+ test('shows error for invalid credentials', async () => {
215
+ await loginPage.login('invalid@example.com', 'wrongpassword');
216
+ await loginPage.expectError('Invalid email or password');
217
+ });
218
+
219
+ test('shows error for empty fields', async () => {
220
+ await loginPage.submitButton.click();
221
+ await loginPage.expectError('Email is required');
222
+ });
223
+ });
224
+ ```
225
+
226
+ ## Test Fixtures
227
+
228
+ ### Authentication Fixture
229
+
230
+ ```typescript
231
+ // fixtures/auth.fixture.ts
232
+ import { test as base, Page } from '@playwright/test';
233
+
234
+ type AuthFixtures = {
235
+ authenticatedPage: Page;
236
+ adminPage: Page;
237
+ };
238
+
239
+ export const test = base.extend<AuthFixtures>({
240
+ authenticatedPage: async ({ page, context }, use) => {
241
+ // Option 1: API login for speed
242
+ const response = await page.request.post('/api/auth/login', {
243
+ data: {
244
+ email: 'user@example.com',
245
+ password: 'password123',
246
+ },
247
+ });
248
+ const { token } = await response.json();
249
+
250
+ // Set token in storage state
251
+ await context.addCookies([{
252
+ name: 'auth_token',
253
+ value: token,
254
+ domain: 'localhost',
255
+ path: '/',
256
+ }]);
257
+
258
+ await use(page);
259
+ },
260
+
261
+ adminPage: async ({ browser }, use) => {
262
+ // Use stored auth state for admin
263
+ const context = await browser.newContext({
264
+ storageState: 'tests/.auth/admin.json',
265
+ });
266
+ const page = await context.newPage();
267
+ await use(page);
268
+ await context.close();
269
+ },
270
+ });
271
+
272
+ export { expect } from '@playwright/test';
273
+
274
+ // Generate and save auth state
275
+ // global-setup.ts
276
+ async function globalSetup() {
277
+ const browser = await chromium.launch();
278
+ const page = await browser.newPage();
279
+
280
+ await page.goto('/login');
281
+ await page.fill('[name=email]', 'admin@example.com');
282
+ await page.fill('[name=password]', 'adminpass');
283
+ await page.click('button[type=submit]');
284
+ await page.waitForURL('/dashboard');
285
+
286
+ await page.context().storageState({ path: 'tests/.auth/admin.json' });
287
+ await browser.close();
288
+ }
289
+
290
+ export default globalSetup;
291
+ ```
292
+
293
+ ## API Testing
294
+
295
+ ### API Test Structure
296
+
297
+ ```typescript
298
+ // api/users.api.spec.ts
299
+ import { test, expect, APIRequestContext } from '@playwright/test';
300
+
301
+ let apiContext: APIRequestContext;
302
+
303
+ test.beforeAll(async ({ playwright }) => {
304
+ apiContext = await playwright.request.newContext({
305
+ baseURL: process.env.API_URL || 'http://localhost:3000/api',
306
+ extraHTTPHeaders: {
307
+ 'Authorization': `Bearer ${process.env.API_TOKEN}`,
308
+ 'Content-Type': 'application/json',
309
+ },
310
+ });
311
+ });
312
+
313
+ test.afterAll(async () => {
314
+ await apiContext.dispose();
315
+ });
316
+
317
+ test.describe('Users API', () => {
318
+ test('GET /users returns paginated list', async () => {
319
+ const response = await apiContext.get('/users?page=1&limit=10');
320
+
321
+ expect(response.ok()).toBeTruthy();
322
+ expect(response.status()).toBe(200);
323
+
324
+ const body = await response.json();
325
+ expect(body.data).toBeInstanceOf(Array);
326
+ expect(body.meta.page).toBe(1);
327
+ expect(body.meta.limit).toBe(10);
328
+ });
329
+
330
+ test('POST /users creates new user', async () => {
331
+ const userData = {
332
+ email: `test-${Date.now()}@example.com`,
333
+ name: 'Test User',
334
+ };
335
+
336
+ const response = await apiContext.post('/users', { data: userData });
337
+
338
+ expect(response.status()).toBe(201);
339
+
340
+ const body = await response.json();
341
+ expect(body.data.email).toBe(userData.email);
342
+ expect(body.data.id).toBeDefined();
343
+
344
+ // Cleanup
345
+ await apiContext.delete(`/users/${body.data.id}`);
346
+ });
347
+
348
+ test('POST /users validates required fields', async () => {
349
+ const response = await apiContext.post('/users', {
350
+ data: { name: 'Missing Email' }
351
+ });
352
+
353
+ expect(response.status()).toBe(400);
354
+
355
+ const body = await response.json();
356
+ expect(body.error.code).toBe('validation_error');
357
+ expect(body.error.details).toContainEqual(
358
+ expect.objectContaining({ field: 'email' })
359
+ );
360
+ });
361
+ });
362
+ ```
363
+
364
+ ## Visual Regression Testing
365
+
366
+ ### Setup with Playwright
367
+
368
+ ```typescript
369
+ // visual.spec.ts
370
+ import { test, expect } from '@playwright/test';
371
+
372
+ test.describe('Visual Regression', () => {
373
+ test('homepage matches snapshot', async ({ page }) => {
374
+ await page.goto('/');
375
+ await page.waitForLoadState('networkidle');
376
+
377
+ // Full page screenshot
378
+ await expect(page).toHaveScreenshot('homepage.png', {
379
+ fullPage: true,
380
+ animations: 'disabled',
381
+ });
382
+ });
383
+
384
+ test('dashboard components match snapshots', async ({ page }) => {
385
+ await page.goto('/dashboard');
386
+
387
+ // Component-level screenshots
388
+ const sidebar = page.locator('[data-testid="sidebar"]');
389
+ await expect(sidebar).toHaveScreenshot('sidebar.png');
390
+
391
+ const header = page.locator('[data-testid="header"]');
392
+ await expect(header).toHaveScreenshot('header.png');
393
+ });
394
+
395
+ test('responsive layouts', async ({ page }) => {
396
+ await page.goto('/');
397
+
398
+ // Desktop
399
+ await page.setViewportSize({ width: 1920, height: 1080 });
400
+ await expect(page).toHaveScreenshot('homepage-desktop.png');
401
+
402
+ // Tablet
403
+ await page.setViewportSize({ width: 768, height: 1024 });
404
+ await expect(page).toHaveScreenshot('homepage-tablet.png');
405
+
406
+ // Mobile
407
+ await page.setViewportSize({ width: 375, height: 667 });
408
+ await expect(page).toHaveScreenshot('homepage-mobile.png');
409
+ });
410
+ });
411
+ ```
412
+
413
+ ## CI/CD Integration
414
+
415
+ ### GitHub Actions Pipeline
416
+
417
+ ```yaml
418
+ # .github/workflows/e2e.yml
419
+ name: E2E Tests
420
+
421
+ on:
422
+ push:
423
+ branches: [main]
424
+ pull_request:
425
+ branches: [main]
426
+
427
+ jobs:
428
+ e2e:
429
+ runs-on: ubuntu-latest
430
+ timeout-minutes: 30
431
+
432
+ services:
433
+ postgres:
434
+ image: postgres:15
435
+ env:
436
+ POSTGRES_PASSWORD: postgres
437
+ ports:
438
+ - 5432:5432
439
+ options: >-
440
+ --health-cmd pg_isready
441
+ --health-interval 10s
442
+ --health-timeout 5s
443
+ --health-retries 5
444
+
445
+ steps:
446
+ - uses: actions/checkout@v4
447
+
448
+ - uses: actions/setup-node@v4
449
+ with:
450
+ node-version: '20'
451
+ cache: 'npm'
452
+
453
+ - name: Install dependencies
454
+ run: npm ci
455
+
456
+ - name: Install Playwright browsers
457
+ run: npx playwright install --with-deps
458
+
459
+ - name: Setup database
460
+ run: npm run db:setup
461
+ env:
462
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
463
+
464
+ - name: Build application
465
+ run: npm run build
466
+
467
+ - name: Run E2E tests
468
+ run: npx playwright test
469
+ env:
470
+ BASE_URL: http://localhost:3000
471
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
472
+
473
+ - name: Upload test results
474
+ uses: actions/upload-artifact@v4
475
+ if: always()
476
+ with:
477
+ name: playwright-report
478
+ path: playwright-report/
479
+ retention-days: 30
480
+
481
+ - name: Upload screenshots on failure
482
+ uses: actions/upload-artifact@v4
483
+ if: failure()
484
+ with:
485
+ name: test-screenshots
486
+ path: test-results/
487
+ retention-days: 7
488
+ ```
489
+
490
+ ### Test Parallelization
491
+
492
+ ```typescript
493
+ // playwright.config.ts additions
494
+ export default defineConfig({
495
+ // Shard tests across multiple CI machines
496
+ // Run with: npx playwright test --shard=1/4
497
+
498
+ // Parallel within machine
499
+ workers: process.env.CI ? 4 : undefined,
500
+ fullyParallel: true,
501
+
502
+ // Retry flaky tests in CI
503
+ retries: process.env.CI ? 2 : 0,
504
+ });
505
+ ```
506
+
507
+ ## Flaky Test Management
508
+
509
+ ### Detection and Prevention
510
+
511
+ ```typescript
512
+ // Avoid: Timing-based waits
513
+ await page.waitForTimeout(5000); // BAD
514
+
515
+ // Prefer: Condition-based waits
516
+ await page.waitForSelector('[data-loaded="true"]'); // GOOD
517
+ await expect(page.locator('.content')).toBeVisible(); // GOOD
518
+
519
+ // Avoid: Fragile selectors
520
+ await page.click('.btn-primary'); // BAD - may match multiple
521
+
522
+ // Prefer: Test IDs or roles
523
+ await page.click('[data-testid="submit-btn"]'); // GOOD
524
+ await page.getByRole('button', { name: 'Submit' }).click(); // BEST
525
+
526
+ // Avoid: Race conditions
527
+ const text = await page.textContent('.counter');
528
+ expect(parseInt(text)).toBe(5); // BAD - might not be updated
529
+
530
+ // Prefer: Assertions that wait
531
+ await expect(page.locator('.counter')).toHaveText('5'); // GOOD
532
+ ```
533
+
534
+ ### Flaky Test Reporter
535
+
536
+ ```typescript
537
+ // flaky-reporter.ts
538
+ import { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
539
+ import * as fs from 'fs';
540
+
541
+ class FlakyReporter implements Reporter {
542
+ private flakyTests: Map<string, number> = new Map();
543
+
544
+ onTestEnd(test: TestCase, result: TestResult) {
545
+ if (result.status === 'passed' && result.retry > 0) {
546
+ // Test passed on retry = flaky
547
+ const key = `${test.location.file}:${test.title}`;
548
+ this.flakyTests.set(key, (this.flakyTests.get(key) || 0) + 1);
549
+ }
550
+ }
551
+
552
+ onEnd() {
553
+ if (this.flakyTests.size > 0) {
554
+ const report = {
555
+ timestamp: new Date().toISOString(),
556
+ flakyTests: Object.fromEntries(this.flakyTests),
557
+ };
558
+ fs.writeFileSync('flaky-tests.json', JSON.stringify(report, null, 2));
559
+ console.log(`Found ${this.flakyTests.size} flaky tests`);
560
+ }
561
+ }
562
+ }
563
+
564
+ export default FlakyReporter;
565
+ ```
566
+
567
+ ## Test Data Management
568
+
569
+ ### Factory Pattern
570
+
571
+ ```typescript
572
+ // factories/user.factory.ts
573
+ import { faker } from '@faker-js/faker';
574
+
575
+ interface User {
576
+ email: string;
577
+ name: string;
578
+ role: 'admin' | 'user';
579
+ }
580
+
581
+ export function createUser(overrides: Partial<User> = {}): User {
582
+ return {
583
+ email: faker.internet.email(),
584
+ name: faker.person.fullName(),
585
+ role: 'user',
586
+ ...overrides,
587
+ };
588
+ }
589
+
590
+ export function createUsers(count: number, overrides: Partial<User> = {}): User[] {
591
+ return Array.from({ length: count }, () => createUser(overrides));
592
+ }
593
+
594
+ // Database seeding
595
+ // seed.ts
596
+ import { prisma } from './db';
597
+ import { createUser, createUsers } from './factories/user.factory';
598
+
599
+ async function seed() {
600
+ // Create admin
601
+ await prisma.user.create({
602
+ data: createUser({ email: 'admin@test.com', role: 'admin' }),
603
+ });
604
+
605
+ // Create test users
606
+ const users = createUsers(10);
607
+ await prisma.user.createMany({ data: users });
608
+ }
609
+ ```
610
+
611
+ ### Test Isolation
612
+
613
+ ```typescript
614
+ // Reset database between tests
615
+ test.beforeEach(async () => {
616
+ await prisma.$executeRaw`TRUNCATE users, orders CASCADE`;
617
+ });
618
+
619
+ // Or use transactions that roll back
620
+ test.beforeEach(async () => {
621
+ await prisma.$executeRaw`BEGIN`;
622
+ });
623
+
624
+ test.afterEach(async () => {
625
+ await prisma.$executeRaw`ROLLBACK`;
626
+ });
627
+ ```
628
+
629
+ ## Performance Testing
630
+
631
+ ### k6 Load Testing
632
+
633
+ ```javascript
634
+ // load-test.js
635
+ import http from 'k6/http';
636
+ import { check, sleep } from 'k6';
637
+
638
+ export const options = {
639
+ stages: [
640
+ { duration: '30s', target: 20 }, // Ramp up
641
+ { duration: '1m', target: 20 }, // Stay at 20
642
+ { duration: '30s', target: 50 }, // Ramp up more
643
+ { duration: '1m', target: 50 }, // Stay at 50
644
+ { duration: '30s', target: 0 }, // Ramp down
645
+ ],
646
+ thresholds: {
647
+ http_req_duration: ['p(95)<500'], // 95% under 500ms
648
+ http_req_failed: ['rate<0.01'], // Error rate under 1%
649
+ },
650
+ };
651
+
652
+ export default function () {
653
+ const res = http.get('http://localhost:3000/api/users');
654
+
655
+ check(res, {
656
+ 'status is 200': (r) => r.status === 200,
657
+ 'response time OK': (r) => r.timings.duration < 500,
658
+ });
659
+
660
+ sleep(1);
661
+ }
662
+ ```
663
+
664
+ ## Anti-Patterns to Avoid
665
+
666
+ | Anti-Pattern | Better Approach |
667
+ |--------------|-----------------|
668
+ | Hard-coded waits | Condition-based waits |
669
+ | Fragile CSS selectors | Test IDs or ARIA roles |
670
+ | Tests depending on order | Independent, isolated tests |
671
+ | Testing third-party services | Mock external dependencies |
672
+ | Ignoring flaky tests | Fix root cause or quarantine |
673
+ | No test data cleanup | Reset state between tests |
674
+ | Giant test files | Organized by feature |
675
+
676
+ ## Test Automation Checklist
677
+
678
+ ### Framework Setup
679
+ - [ ] Framework selected and configured
680
+ - [ ] Page Object Model structure
681
+ - [ ] Test fixtures for common scenarios
682
+ - [ ] Parallel execution enabled
683
+ - [ ] Retries configured for CI
684
+
685
+ ### CI/CD Integration
686
+ - [ ] Tests run on every PR
687
+ - [ ] Test results reported
688
+ - [ ] Screenshots/videos on failure
689
+ - [ ] Flaky test tracking
690
+ - [ ] Performance benchmarks
691
+
692
+ ### Maintenance
693
+ - [ ] Regular review of flaky tests
694
+ - [ ] Test coverage monitoring
695
+ - [ ] Documentation updated
696
+ - [ ] Quarterly cleanup of obsolete tests
697
+
698
+ ## Constraints
699
+
700
+ - Tests must be independent and isolated
701
+ - No hard-coded waits (use conditions)
702
+ - Use semantic selectors (roles, test IDs)
703
+ - Keep tests maintainable over comprehensive
704
+ - Fix flaky tests immediately
705
+
706
+ ## Related Skills
707
+
708
+ - `qa-expert` - Test strategy
709
+ - `devops-engineer` - CI/CD pipelines
710
+ - `frontend-developer` - UI testing
711
+ - `backend-developer` - API testing