start-vibing 1.1.3 → 1.1.4

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.
@@ -1,66 +1,441 @@
1
1
  ---
2
2
  name: test-coverage
3
- description: Creates and manages tests with Playwright E2E and Vitest unit tests. Activates when user says 'create tests', 'add tests', 'test coverage', 'write tests', or after implementing new features that need testing.
3
+ description: Manages test coverage with Playwright E2E and Vitest unit tests. Tracks which files need tests, provides templates with fixture-based cleanup, enforces multi-viewport testing and database validation.
4
4
  allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
5
  ---
6
6
 
7
- # Test Coverage
7
+ # Test Coverage - Testing Management System
8
8
 
9
- ## When to Use
9
+ ## Purpose
10
10
 
11
- - After implementing new features
12
- - When user asks for tests
13
- - Before committing to verify coverage
11
+ This skill manages test coverage with **Playwright E2E** and **Vitest unit tests**:
14
12
 
15
- ## Critical Rules
13
+ - **Detects** new files that need tests
14
+ - **Maps** components consuming API routes (tRPC/REST)
15
+ - **Tracks** which pages have E2E coverage
16
+ - **Generates** test templates with cleanup
17
+ - **Validates** real authentication usage (MANDATORY)
18
+ - **Ensures** database validation after UI actions
19
+ - **Enforces** multi-viewport testing
16
20
 
17
- 1. **Cleanup all test data** - use fixture-based tracking
18
- 2. **Verify in database** - check DB state after UI actions
19
- 3. **Test all viewports** - desktop, tablet, mobile
20
- 4. **Real auth only** - never mock authentication
21
- 5. **Unique data** - timestamps in emails/names
22
- 6. **No .skip()** - never skip tests
21
+ ---
22
+
23
+ ## CRITICAL RULES
23
24
 
24
- ## Structure
25
+ > **MANDATORY - NO EXCEPTIONS:**
26
+
27
+ 1. **CLEANUP ALL TEST DATA** - Use fixture-based tracking
28
+ 2. **VERIFY IN DATABASE** - Check DB state after UI actions
29
+ 3. **TEST ALL VIEWPORTS** - Desktop, tablet, iPhone SE minimum
30
+ 4. **REAL AUTH ONLY** - Never mock authentication
31
+ 5. **UNIQUE DATA** - Timestamps in emails/names
32
+ 6. **NO SKIP** - Never use `.skip()` or `.only()`
33
+
34
+ ---
35
+
36
+ ## Test Structure
25
37
 
26
38
  ```
27
39
  tests/
28
- ├── unit/ # Vitest
40
+ ├── unit/ # Unit tests (Vitest)
29
41
  │ └── *.test.ts
30
- └── e2e/ # Playwright
31
- ├── fixtures/ # Auth, DB, cleanup
32
- ├── pages/ # Page Object Model
33
- ├── flows/ # User journey tests
34
- └── api/ # API tests
42
+ └── e2e/ # E2E tests (Playwright)
43
+ ├── fixtures/
44
+ ├── index.ts # Custom fixtures (auth, db, cleanup)
45
+ ├── auth.fixture.ts # Authentication helpers
46
+ └── db.fixture.ts # Database connection & cleanup
47
+ ├── pages/ # Page Object Model
48
+ │ ├── base.page.ts # Base page with common methods
49
+ │ ├── login.page.ts
50
+ │ └── register.page.ts
51
+ ├── flows/ # User flow tests
52
+ │ ├── auth.spec.ts # Login, register, logout
53
+ │ ├── crud.spec.ts # Create, read, update, delete
54
+ │ └── permissions.spec.ts
55
+ ├── api/ # API-only tests (no UI)
56
+ │ ├── rest.spec.ts # REST API tests
57
+ │ └── trpc.spec.ts # tRPC API tests
58
+ └── playwright.config.ts
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Cleanup Fixture (MANDATORY)
64
+
65
+ Every project MUST have cleanup fixtures:
66
+
67
+ ```typescript
68
+ // tests/e2e/fixtures/index.ts
69
+ import { test as base, expect } from '@playwright/test';
70
+ import { MongoClient, Db, ObjectId } from 'mongodb';
71
+
72
+ type TestFixtures = {
73
+ db: Db;
74
+ createdIds: Map<string, ObjectId[]>;
75
+ trackCreated: (collection: string, id: ObjectId) => void;
76
+ };
77
+
78
+ export const test = base.extend<TestFixtures>({
79
+ db: async ({}, use) => {
80
+ const client = await MongoClient.connect(process.env.MONGODB_URI!);
81
+ const db = client.db();
82
+ await use(db);
83
+ await client.close();
84
+ },
85
+
86
+ createdIds: async ({}, use) => {
87
+ const ids = new Map<string, ObjectId[]>();
88
+ await use(ids);
89
+ },
90
+
91
+ trackCreated: async ({ createdIds }, use) => {
92
+ const track = (collection: string, id: ObjectId) => {
93
+ const existing = createdIds.get(collection) || [];
94
+ existing.push(id);
95
+ createdIds.set(collection, existing);
96
+ };
97
+ await use(track);
98
+ },
99
+
100
+ // AUTO-CLEANUP runs EVEN IF test fails
101
+ }, async ({ db, createdIds }, use) => {
102
+ await use();
103
+
104
+ for (const [collection, ids] of createdIds.entries()) {
105
+ if (ids.length > 0) {
106
+ await db.collection(collection).deleteMany({
107
+ _id: { $in: ids }
108
+ });
109
+ }
110
+ }
111
+ });
112
+
113
+ export { expect };
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Auth Helper (MANDATORY)
119
+
120
+ ```typescript
121
+ // tests/e2e/fixtures/auth.fixture.ts
122
+ import { Page } from '@playwright/test';
123
+
124
+ export interface TestUser {
125
+ name: string;
126
+ email: string;
127
+ password: string;
128
+ }
129
+
130
+ export function generateTestUser(): TestUser {
131
+ const timestamp = Date.now();
132
+ const random = Math.random().toString(36).substring(7);
133
+ return {
134
+ name: `Test User ${timestamp}`,
135
+ email: `testuser_${timestamp}_${random}@test.com`,
136
+ password: 'TestPassword123!',
137
+ };
138
+ }
139
+
140
+ export async function registerUser(page: Page, user: TestUser): Promise<void> {
141
+ await page.goto('/auth/register');
142
+ await page.getByTestId('name-input').fill(user.name);
143
+ await page.getByTestId('email-input').fill(user.email);
144
+ await page.getByTestId('password-input').fill(user.password);
145
+ await page.getByTestId('confirm-password-input').fill(user.password);
146
+ await page.getByTestId('submit-button').click();
147
+ await page.waitForURL(/\/app/, { timeout: 10000 });
148
+ }
149
+
150
+ export async function loginUser(page: Page, user: TestUser): Promise<void> {
151
+ await page.goto('/auth/login');
152
+ await page.getByTestId('email-input').fill(user.email);
153
+ await page.getByTestId('password-input').fill(user.password);
154
+ await page.getByTestId('submit-button').click();
155
+ await page.waitForURL(/\/app/, { timeout: 10000 });
156
+ }
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Test Templates
162
+
163
+ ### E2E Flow Test (with cleanup)
164
+
165
+ ```typescript
166
+ import { test, expect } from '../fixtures';
167
+ import { generateTestUser, registerUser } from '../fixtures/auth.fixture';
168
+
169
+ test.describe('[Feature] Flow', () => {
170
+ test('complete user journey', async ({ page, db, trackCreated }) => {
171
+ const user = generateTestUser();
172
+
173
+ // 1. Register
174
+ await registerUser(page, user);
175
+
176
+ // 2. Verify in database
177
+ const dbUser = await db.collection('users').findOne({
178
+ email: user.email
179
+ });
180
+ expect(dbUser).toBeTruthy();
181
+ trackCreated('users', dbUser!._id); // TRACK FOR CLEANUP
182
+
183
+ // 3. Create item
184
+ await page.goto('/items/new');
185
+ await page.getByTestId('title-input').fill('Test Item');
186
+ await page.getByTestId('submit-button').click();
187
+
188
+ // 4. Verify item in DB
189
+ const item = await db.collection('items').findOne({
190
+ userId: dbUser!._id
191
+ });
192
+ expect(item).toBeTruthy();
193
+ trackCreated('items', item!._id); // TRACK FOR CLEANUP
194
+
195
+ // Test continues... cleanup is automatic
196
+ });
197
+ });
198
+ ```
199
+
200
+ ### Multi-Viewport Test
201
+
202
+ ```typescript
203
+ import { test, expect } from '../fixtures';
204
+
205
+ test.describe('Responsive Design', () => {
206
+ const viewports = [
207
+ { name: 'mobile', width: 375, height: 667 },
208
+ { name: 'tablet', width: 768, height: 1024 },
209
+ { name: 'desktop', width: 1280, height: 800 },
210
+ ];
211
+
212
+ for (const viewport of viewports) {
213
+ test(`layout works on ${viewport.name}`, async ({ page }) => {
214
+ await page.setViewportSize(viewport);
215
+ await page.goto('/');
216
+
217
+ if (viewport.width < 768) {
218
+ // Mobile: hamburger menu
219
+ await expect(page.getByTestId('hamburger-menu')).toBeVisible();
220
+ await expect(page.getByTestId('sidebar')).toBeHidden();
221
+ } else {
222
+ // Desktop: sidebar visible
223
+ await expect(page.getByTestId('sidebar')).toBeVisible();
224
+ }
225
+ });
226
+ }
227
+ });
228
+ ```
229
+
230
+ ### API Test (REST)
231
+
232
+ ```typescript
233
+ import { test, expect } from '@playwright/test';
234
+
235
+ test.describe('REST API', () => {
236
+ test('requires authentication', async ({ request }) => {
237
+ const response = await request.get('/api/users');
238
+ expect(response.status()).toBe(401);
239
+ });
240
+
241
+ test('validates input', async ({ request }) => {
242
+ const response = await request.post('/api/users', {
243
+ data: { email: 'invalid' }
244
+ });
245
+ expect(response.status()).toBe(400);
246
+ });
247
+ });
35
248
  ```
36
249
 
37
- ## Commands
250
+ ### API Test (tRPC)
251
+
252
+ ```typescript
253
+ import { test, expect } from '@playwright/test';
254
+
255
+ test.describe('tRPC API', () => {
256
+ test('query without auth fails', async ({ request }) => {
257
+ const response = await request.get('/api/trpc/user.me');
258
+ expect(response.status()).toBe(401);
259
+ });
260
+
261
+ test('mutation validates input', async ({ request }) => {
262
+ const response = await request.post('/api/trpc/user.create', {
263
+ data: { json: { name: '' } }
264
+ });
265
+ const body = await response.json();
266
+ expect(body.error).toBeDefined();
267
+ });
268
+ });
269
+ ```
270
+
271
+ ### Security Test
272
+
273
+ ```typescript
274
+ import { test, expect } from '../fixtures';
275
+
276
+ test.describe('Security - Forbidden Requests', () => {
277
+ test('cannot access other users data', async ({ page, db }) => {
278
+ const userA = await createTestUser(db);
279
+ const userB = await createTestUser(db);
280
+
281
+ await loginAs(page, userA);
282
+
283
+ // Try to access user B's data
284
+ const response = await page.request.get(`/api/users/${userB._id}`);
285
+ expect(response.status()).toBe(403);
286
+
287
+ // Verify nothing changed in DB
288
+ const unchanged = await db.collection('users').findOne({
289
+ _id: userB._id
290
+ });
291
+ expect(unchanged).toEqual(userB);
292
+ });
293
+ });
294
+ ```
295
+
296
+ ### Unit Test
297
+
298
+ ```typescript
299
+ import { describe, it, expect } from 'vitest';
300
+
301
+ describe('[Feature]', () => {
302
+ describe('success cases', () => {
303
+ it('should [expected behavior] when [condition]', () => {
304
+ // Arrange
305
+ // Act
306
+ // Assert
307
+ });
308
+ });
309
+
310
+ describe('error cases', () => {
311
+ it('should throw when [invalid condition]', () => {
312
+ expect(() => fn(invalid)).toThrow();
313
+ });
314
+ });
315
+ });
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Files That NEED Tests
321
+
322
+ | Type | Location | Test Expected | Required |
323
+ | --------- | --------------------- | --------------------- | -------------- |
324
+ | API Route | `server/routers/*.ts` | `tests/unit/*.test.ts` | **YES** |
325
+ | API Route | `app/api/**/*.ts` | `tests/e2e/api/*.spec.ts` | **YES** |
326
+ | Model | `server/models/*.ts` | `tests/unit/*.test.ts` | **YES** |
327
+ | Page | `app/**/page.tsx` | `tests/e2e/flows/*.spec.ts` | **YES** |
328
+ | Component | `components/**/*.tsx` | `tests/e2e/*.spec.ts` | If interactive |
329
+ | Hook | `hooks/*.ts` | `tests/unit/*.test.ts` | YES |
330
+ | Util | `lib/*.ts` | `tests/unit/*.test.ts` | If exported |
331
+
332
+ ---
333
+
334
+ ## Required Flows (E2E)
335
+
336
+ Every app MUST have tests for:
337
+
338
+ - [ ] **Registration** - Create new user, verify in DB
339
+ - [ ] **Login/Logout** - Auth state changes correctly
340
+ - [ ] **CRUD Create** - Item created, visible, in DB
341
+ - [ ] **CRUD Read** - Item displayed correctly
342
+ - [ ] **CRUD Update** - Item updated, changes in DB
343
+ - [ ] **CRUD Delete** - Item removed from DB
344
+ - [ ] **Permissions** - Forbidden requests blocked
345
+ - [ ] **Responsive** - Works on all viewports
346
+
347
+ ---
348
+
349
+ ## Required data-testid
350
+
351
+ ```html
352
+ <!-- Forms -->
353
+ <input data-testid="name-input" />
354
+ <input data-testid="email-input" />
355
+ <input data-testid="password-input" />
356
+ <input data-testid="confirm-password-input" />
357
+ <button data-testid="submit-button" />
358
+
359
+ <!-- Feedback -->
360
+ <div data-testid="error-message" />
361
+ <div data-testid="success-message" />
362
+ <div data-testid="loading-spinner" />
363
+
364
+ <!-- Navigation -->
365
+ <nav data-testid="sidebar" />
366
+ <button data-testid="hamburger-menu" />
367
+ <nav data-testid="mobile-nav" />
368
+ <button data-testid="logout-button" />
369
+
370
+ <!-- Actions -->
371
+ <button data-testid="delete-button" />
372
+ <button data-testid="edit-button" />
373
+ <button data-testid="confirm-delete" />
374
+ ```
375
+
376
+ ---
377
+
378
+ ## Playwright Commands
38
379
 
39
380
  ```bash
40
- # Run all
41
- bun run test && bun run test:e2e
381
+ # Install
382
+ bun add -D @playwright/test
383
+ bunx playwright install
42
384
 
43
- # Playwright
44
- bunx playwright test
45
- bunx playwright test --ui
46
- bunx playwright test --headed
385
+ # Run
386
+ bunx playwright test # All tests
387
+ bunx playwright test --ui # UI mode (recommended)
388
+ bunx playwright test --headed # See browser
389
+ bunx playwright test --debug # Debug mode
390
+
391
+ # Specific
392
+ bunx playwright test flows/auth # Specific folder
393
+ bunx playwright test --project="iPhone SE" # Specific viewport
394
+
395
+ # Reports
396
+ bunx playwright show-report
47
397
  ```
48
398
 
49
- ## Required Tests
399
+ ---
50
400
 
51
- | File Type | Test Required |
52
- |-----------|---------------|
53
- | API routes | Unit + E2E |
54
- | Pages | E2E flows |
55
- | Components | E2E if interactive |
56
- | Hooks/Utils | Unit tests |
401
+ ## Checklist
57
402
 
58
- ## Required Flows
403
+ ### Before Commit
404
+
405
+ - [ ] All new features have E2E tests?
406
+ - [ ] Tests use fixtures for cleanup?
407
+ - [ ] All created data is tracked and cleaned?
408
+ - [ ] Database state verified after UI actions?
409
+ - [ ] Tests run on all viewports?
410
+ - [ ] Forbidden requests tested?
411
+ - [ ] No `.skip()` in tests?
412
+ - [ ] `bunx playwright test` passes?
413
+ - [ ] `bun run test` passes?
414
+
415
+ ---
416
+
417
+ ## FORBIDDEN (Never Do)
418
+
419
+ ```typescript
420
+ // WRONG - Skipping tests
421
+ test.skip("should work when authenticated", ...);
422
+
423
+ // WRONG - Mocking auth
424
+ const mockAuth = () => { /* fake */ };
425
+
426
+ // WRONG - Fixed test user
427
+ const testUser = { email: "test@test.com" };
428
+
429
+ // WRONG - No cleanup
430
+ const user = await db.create({ ... }); // Orphaned!
431
+
432
+ // WRONG - No DB validation
433
+ await page.click('submit');
434
+ // Just trust the UI? NO!
435
+ ```
436
+
437
+ ---
59
438
 
60
- - [ ] Registration - create user, verify in DB
61
- - [ ] Login/Logout - auth state changes
62
- - [ ] CRUD - create, read, update, delete
63
- - [ ] Permissions - forbidden requests blocked
64
- - [ ] Responsive - all viewports
439
+ ## Version
65
440
 
66
- See `templates/` for code examples.
441
+ - **v3.0.0** - Complete E2E architecture with cleanup, DB validation, multi-viewport