@plazmodium/odin 0.3.2-beta → 0.3.4-beta
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/README.md +82 -11
- package/builtin/ODIN.md +1045 -0
- package/builtin/agent-definitions/README.md +170 -0
- package/builtin/agent-definitions/_shared-context.md +377 -0
- package/builtin/agent-definitions/architect.md +627 -0
- package/builtin/agent-definitions/builder.md +716 -0
- package/builtin/agent-definitions/discovery.md +293 -0
- package/builtin/agent-definitions/documenter.md +238 -0
- package/builtin/agent-definitions/guardian.md +1049 -0
- package/builtin/agent-definitions/integrator.md +363 -0
- package/builtin/agent-definitions/planning.md +236 -0
- package/builtin/agent-definitions/product.md +405 -0
- package/builtin/agent-definitions/release.md +430 -0
- package/builtin/agent-definitions/reviewer.md +447 -0
- package/builtin/agent-definitions/watcher.md +402 -0
- package/builtin/skills/api/graphql/SKILL.md +548 -0
- package/builtin/skills/api/grpc/SKILL.md +554 -0
- package/builtin/skills/api/rest-api/SKILL.md +469 -0
- package/builtin/skills/api/trpc/SKILL.md +503 -0
- package/builtin/skills/architecture/clean-architecture/SKILL.md +141 -0
- package/builtin/skills/architecture/domain-driven-design/SKILL.md +129 -0
- package/builtin/skills/architecture/event-driven/SKILL.md +145 -0
- package/builtin/skills/architecture/microservices/SKILL.md +143 -0
- package/builtin/skills/architecture/tla-precheck/SKILL.md +171 -0
- package/builtin/skills/backend/golang-gin/SKILL.md +141 -0
- package/builtin/skills/backend/nodejs-express/SKILL.md +277 -0
- package/builtin/skills/backend/nodejs-fastify/SKILL.md +152 -0
- package/builtin/skills/backend/python-django/SKILL.md +128 -0
- package/builtin/skills/backend/python-fastapi/SKILL.md +140 -0
- package/builtin/skills/database/mongodb/SKILL.md +132 -0
- package/builtin/skills/database/postgresql/SKILL.md +120 -0
- package/builtin/skills/database/prisma-orm/SKILL.md +366 -0
- package/builtin/skills/database/redis/SKILL.md +140 -0
- package/builtin/skills/database/supabase/SKILL.md +416 -0
- package/builtin/skills/devops/aws/SKILL.md +382 -0
- package/builtin/skills/devops/docker/SKILL.md +359 -0
- package/builtin/skills/devops/github-actions/SKILL.md +435 -0
- package/builtin/skills/devops/kubernetes/SKILL.md +459 -0
- package/builtin/skills/devops/terraform/SKILL.md +453 -0
- package/builtin/skills/frontend/alpine-dev/SKILL.md +27 -0
- package/builtin/skills/frontend/angular-dev/SKILL.md +28 -0
- package/builtin/skills/frontend/astro-dev/SKILL.md +28 -0
- package/builtin/skills/frontend/htmx-dev/SKILL.md +28 -0
- package/builtin/skills/frontend/nextjs-dev/SKILL.md +470 -0
- package/builtin/skills/frontend/react-patterns/SKILL.md +166 -0
- package/builtin/skills/frontend/svelte-dev/SKILL.md +28 -0
- package/builtin/skills/frontend/tailwindcss/SKILL.md +131 -0
- package/builtin/skills/frontend/vuejs-dev/SKILL.md +28 -0
- package/builtin/skills/generic-dev/SKILL.md +307 -0
- package/builtin/skills/testing/cypress/SKILL.md +372 -0
- package/builtin/skills/testing/jest/SKILL.md +176 -0
- package/builtin/skills/testing/playwright/SKILL.md +341 -0
- package/builtin/skills/testing/unit-tests-eval-sdd/SKILL.md +73 -0
- package/builtin/skills/testing/unit-tests-sdd/SKILL.md +83 -0
- package/builtin/skills/testing/vitest/SKILL.md +249 -0
- package/dist/adapters/skills/filesystem.d.ts.map +1 -1
- package/dist/adapters/skills/filesystem.js +2 -18
- package/dist/adapters/skills/filesystem.js.map +1 -1
- package/dist/builtin-assets.d.ts +8 -0
- package/dist/builtin-assets.d.ts.map +1 -0
- package/dist/builtin-assets.js +90 -0
- package/dist/builtin-assets.js.map +1 -0
- package/dist/init.js +69 -11
- package/dist/init.js.map +1 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/prepare-phase-context.d.ts.map +1 -1
- package/dist/tools/prepare-phase-context.js +5 -0
- package/dist/tools/prepare-phase-context.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: playwright
|
|
3
|
+
description: Playwright end-to-end testing expertise for modern web applications. Covers browser automation, cross-browser testing, API testing, visual regression, and CI integration. Use for E2E testing of web apps across Chromium, Firefox, and WebKit.
|
|
4
|
+
category: testing
|
|
5
|
+
compatible_with:
|
|
6
|
+
- nextjs-dev
|
|
7
|
+
- react-patterns
|
|
8
|
+
- github-actions
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Playwright End-to-End Testing
|
|
12
|
+
|
|
13
|
+
## Instructions
|
|
14
|
+
|
|
15
|
+
1. **Assess the testing scope**: Determine if it's E2E tests, API tests, or visual regression.
|
|
16
|
+
2. **Follow Playwright conventions**:
|
|
17
|
+
- Test files: `*.spec.ts` in `tests/` or `e2e/` directory
|
|
18
|
+
- Use `test` and `expect` from `@playwright/test`
|
|
19
|
+
- Leverage auto-waiting and web-first assertions
|
|
20
|
+
3. **Provide complete examples**: Include page objects, fixtures, and proper selectors.
|
|
21
|
+
4. **Guide on best practices**: Locators, parallelization, debugging.
|
|
22
|
+
5. **Cover advanced features**: Network mocking, authentication, visual comparison.
|
|
23
|
+
|
|
24
|
+
## Test Structure
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { test, expect } from '@playwright/test';
|
|
28
|
+
|
|
29
|
+
test.describe('User Authentication', () => {
|
|
30
|
+
test.beforeEach(async ({ page }) => {
|
|
31
|
+
await page.goto('/login');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('should login with valid credentials', async ({ page }) => {
|
|
35
|
+
await page.fill('[data-testid="email"]', 'user@example.com');
|
|
36
|
+
await page.fill('[data-testid="password"]', 'password123');
|
|
37
|
+
await page.click('[data-testid="submit"]');
|
|
38
|
+
|
|
39
|
+
await expect(page).toHaveURL('/dashboard');
|
|
40
|
+
await expect(page.getByRole('heading')).toHaveText('Welcome');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should show error for invalid credentials', async ({ page }) => {
|
|
44
|
+
await page.fill('[data-testid="email"]', 'invalid@example.com');
|
|
45
|
+
await page.fill('[data-testid="password"]', 'wrong');
|
|
46
|
+
await page.click('[data-testid="submit"]');
|
|
47
|
+
|
|
48
|
+
await expect(page.getByText('Invalid credentials')).toBeVisible();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Locators (Recommended Order)
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// 1. Role-based (most resilient)
|
|
57
|
+
page.getByRole('button', { name: 'Submit' });
|
|
58
|
+
page.getByRole('textbox', { name: 'Email' });
|
|
59
|
+
page.getByRole('link', { name: 'Sign up' });
|
|
60
|
+
|
|
61
|
+
// 2. Label/placeholder text
|
|
62
|
+
page.getByLabel('Email address');
|
|
63
|
+
page.getByPlaceholder('Enter your email');
|
|
64
|
+
|
|
65
|
+
// 3. Text content
|
|
66
|
+
page.getByText('Welcome back');
|
|
67
|
+
page.getByText(/welcome/i); // Case-insensitive regex
|
|
68
|
+
|
|
69
|
+
// 4. Test IDs (explicit)
|
|
70
|
+
page.getByTestId('submit-button');
|
|
71
|
+
|
|
72
|
+
// 5. CSS selectors (last resort)
|
|
73
|
+
page.locator('.submit-btn');
|
|
74
|
+
page.locator('#email-input');
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Common Actions
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// Navigation
|
|
81
|
+
await page.goto('https://example.com');
|
|
82
|
+
await page.goBack();
|
|
83
|
+
await page.reload();
|
|
84
|
+
|
|
85
|
+
// Clicking
|
|
86
|
+
await page.click('button');
|
|
87
|
+
await page.dblclick('button');
|
|
88
|
+
await page.click('button', { button: 'right' });
|
|
89
|
+
|
|
90
|
+
// Typing
|
|
91
|
+
await page.fill('input', 'text'); // Clear and type
|
|
92
|
+
await page.type('input', 'text'); // Type character by character
|
|
93
|
+
await page.press('input', 'Enter'); // Press key
|
|
94
|
+
|
|
95
|
+
// Selection
|
|
96
|
+
await page.selectOption('select', 'value');
|
|
97
|
+
await page.selectOption('select', { label: 'Option' });
|
|
98
|
+
|
|
99
|
+
// Checkboxes/Radio
|
|
100
|
+
await page.check('input[type="checkbox"]');
|
|
101
|
+
await page.uncheck('input[type="checkbox"]');
|
|
102
|
+
|
|
103
|
+
// File upload
|
|
104
|
+
await page.setInputFiles('input[type="file"]', 'path/to/file.pdf');
|
|
105
|
+
|
|
106
|
+
// Hover
|
|
107
|
+
await page.hover('.dropdown-trigger');
|
|
108
|
+
|
|
109
|
+
// Drag and drop
|
|
110
|
+
await page.dragAndDrop('#source', '#target');
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Assertions
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Visibility
|
|
117
|
+
await expect(locator).toBeVisible();
|
|
118
|
+
await expect(locator).toBeHidden();
|
|
119
|
+
|
|
120
|
+
// Text
|
|
121
|
+
await expect(locator).toHaveText('exact text');
|
|
122
|
+
await expect(locator).toContainText('partial');
|
|
123
|
+
await expect(locator).toHaveValue('input value');
|
|
124
|
+
|
|
125
|
+
// Attributes
|
|
126
|
+
await expect(locator).toHaveAttribute('href', '/link');
|
|
127
|
+
await expect(locator).toHaveClass(/active/);
|
|
128
|
+
await expect(locator).toBeEnabled();
|
|
129
|
+
await expect(locator).toBeDisabled();
|
|
130
|
+
await expect(locator).toBeChecked();
|
|
131
|
+
|
|
132
|
+
// Count
|
|
133
|
+
await expect(locator).toHaveCount(3);
|
|
134
|
+
|
|
135
|
+
// Page
|
|
136
|
+
await expect(page).toHaveURL(/dashboard/);
|
|
137
|
+
await expect(page).toHaveTitle('Page Title');
|
|
138
|
+
|
|
139
|
+
// Screenshots
|
|
140
|
+
await expect(page).toHaveScreenshot('homepage.png');
|
|
141
|
+
await expect(locator).toHaveScreenshot('component.png');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Page Object Model
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// pages/LoginPage.ts
|
|
148
|
+
import { Page, Locator } from '@playwright/test';
|
|
149
|
+
|
|
150
|
+
export class LoginPage {
|
|
151
|
+
readonly page: Page;
|
|
152
|
+
readonly emailInput: Locator;
|
|
153
|
+
readonly passwordInput: Locator;
|
|
154
|
+
readonly submitButton: Locator;
|
|
155
|
+
|
|
156
|
+
constructor(page: Page) {
|
|
157
|
+
this.page = page;
|
|
158
|
+
this.emailInput = page.getByLabel('Email');
|
|
159
|
+
this.passwordInput = page.getByLabel('Password');
|
|
160
|
+
this.submitButton = page.getByRole('button', { name: 'Sign in' });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async goto() {
|
|
164
|
+
await this.page.goto('/login');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async login(email: string, password: string) {
|
|
168
|
+
await this.emailInput.fill(email);
|
|
169
|
+
await this.passwordInput.fill(password);
|
|
170
|
+
await this.submitButton.click();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// tests/login.spec.ts
|
|
175
|
+
import { test, expect } from '@playwright/test';
|
|
176
|
+
import { LoginPage } from './pages/LoginPage';
|
|
177
|
+
|
|
178
|
+
test('login flow', async ({ page }) => {
|
|
179
|
+
const loginPage = new LoginPage(page);
|
|
180
|
+
await loginPage.goto();
|
|
181
|
+
await loginPage.login('user@example.com', 'password');
|
|
182
|
+
await expect(page).toHaveURL('/dashboard');
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Fixtures
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// fixtures.ts
|
|
190
|
+
import { test as base } from '@playwright/test';
|
|
191
|
+
import { LoginPage } from './pages/LoginPage';
|
|
192
|
+
|
|
193
|
+
type Fixtures = {
|
|
194
|
+
loginPage: LoginPage;
|
|
195
|
+
authenticatedPage: Page;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export const test = base.extend<Fixtures>({
|
|
199
|
+
loginPage: async ({ page }, use) => {
|
|
200
|
+
const loginPage = new LoginPage(page);
|
|
201
|
+
await use(loginPage);
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
authenticatedPage: async ({ page }, use) => {
|
|
205
|
+
// Setup: Login
|
|
206
|
+
await page.goto('/login');
|
|
207
|
+
await page.fill('#email', 'user@example.com');
|
|
208
|
+
await page.fill('#password', 'password');
|
|
209
|
+
await page.click('button[type="submit"]');
|
|
210
|
+
await page.waitForURL('/dashboard');
|
|
211
|
+
|
|
212
|
+
await use(page);
|
|
213
|
+
|
|
214
|
+
// Teardown: Logout
|
|
215
|
+
await page.click('[data-testid="logout"]');
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## API Testing
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { test, expect } from '@playwright/test';
|
|
224
|
+
|
|
225
|
+
test('API: create user', async ({ request }) => {
|
|
226
|
+
const response = await request.post('/api/users', {
|
|
227
|
+
data: {
|
|
228
|
+
email: 'new@example.com',
|
|
229
|
+
name: 'New User',
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
expect(response.ok()).toBeTruthy();
|
|
234
|
+
const user = await response.json();
|
|
235
|
+
expect(user.email).toBe('new@example.com');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test('API: with authentication', async ({ request }) => {
|
|
239
|
+
const response = await request.get('/api/profile', {
|
|
240
|
+
headers: {
|
|
241
|
+
Authorization: `Bearer ${process.env.API_TOKEN}`,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(response.status()).toBe(200);
|
|
246
|
+
});
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Network Mocking
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
test('mock API response', async ({ page }) => {
|
|
253
|
+
await page.route('/api/users', async (route) => {
|
|
254
|
+
await route.fulfill({
|
|
255
|
+
status: 200,
|
|
256
|
+
contentType: 'application/json',
|
|
257
|
+
body: JSON.stringify([{ id: 1, name: 'Mocked User' }]),
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
await page.goto('/users');
|
|
262
|
+
await expect(page.getByText('Mocked User')).toBeVisible();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('modify response', async ({ page }) => {
|
|
266
|
+
await page.route('/api/data', async (route) => {
|
|
267
|
+
const response = await route.fetch();
|
|
268
|
+
const json = await response.json();
|
|
269
|
+
json.modified = true;
|
|
270
|
+
await route.fulfill({ response, json });
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Configuration (playwright.config.ts)
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
279
|
+
|
|
280
|
+
export default defineConfig({
|
|
281
|
+
testDir: './tests',
|
|
282
|
+
fullyParallel: true,
|
|
283
|
+
forbidOnly: !!process.env.CI,
|
|
284
|
+
retries: process.env.CI ? 2 : 0,
|
|
285
|
+
workers: process.env.CI ? 1 : undefined,
|
|
286
|
+
reporter: [
|
|
287
|
+
['html'],
|
|
288
|
+
['junit', { outputFile: 'results.xml' }],
|
|
289
|
+
],
|
|
290
|
+
use: {
|
|
291
|
+
baseURL: 'http://localhost:3000',
|
|
292
|
+
trace: 'on-first-retry',
|
|
293
|
+
screenshot: 'only-on-failure',
|
|
294
|
+
video: 'retain-on-failure',
|
|
295
|
+
},
|
|
296
|
+
projects: [
|
|
297
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
298
|
+
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
299
|
+
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
|
300
|
+
{ name: 'mobile', use: { ...devices['iPhone 13'] } },
|
|
301
|
+
],
|
|
302
|
+
webServer: {
|
|
303
|
+
command: 'npm run dev',
|
|
304
|
+
url: 'http://localhost:3000',
|
|
305
|
+
reuseExistingServer: !process.env.CI,
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Best Practices
|
|
311
|
+
|
|
312
|
+
- **Use role-based locators** - Most resilient to UI changes
|
|
313
|
+
- **Avoid hard waits** - Use `waitForSelector`, `waitForURL`, auto-waiting
|
|
314
|
+
- **Test user flows, not implementation** - Think like a user
|
|
315
|
+
- **Use Page Object Model** for large test suites
|
|
316
|
+
- **Run in CI** with trace and video on failure
|
|
317
|
+
- **Parallelize tests** - Playwright is built for parallel execution
|
|
318
|
+
- **Use fixtures** for common setup/teardown
|
|
319
|
+
- **Visual regression** for UI-heavy apps
|
|
320
|
+
|
|
321
|
+
## Debugging
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# Run with UI mode
|
|
325
|
+
npx playwright test --ui
|
|
326
|
+
|
|
327
|
+
# Run with headed browser
|
|
328
|
+
npx playwright test --headed
|
|
329
|
+
|
|
330
|
+
# Debug specific test
|
|
331
|
+
npx playwright test --debug tests/login.spec.ts
|
|
332
|
+
|
|
333
|
+
# Generate code
|
|
334
|
+
npx playwright codegen http://localhost:3000
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## References
|
|
338
|
+
|
|
339
|
+
- Playwright Documentation: https://playwright.dev/docs/intro
|
|
340
|
+
- Best Practices: https://playwright.dev/docs/best-practices
|
|
341
|
+
- API Reference: https://playwright.dev/docs/api/class-playwright
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unit-tests-eval-sdd
|
|
3
|
+
description: Mandatory unit test evaluation protocol for Reviewer. Scores test quality, verifies compliance, and sends weak test implementations back to Builder for rework.
|
|
4
|
+
category: testing
|
|
5
|
+
compatible_with:
|
|
6
|
+
- vitest
|
|
7
|
+
- jest
|
|
8
|
+
- react-patterns
|
|
9
|
+
- nextjs-dev
|
|
10
|
+
- nodejs-fastify
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Unit Test Evaluation Protocol
|
|
14
|
+
|
|
15
|
+
Use this skill during Reviewer when the feature includes new or changed tests.
|
|
16
|
+
|
|
17
|
+
## Reviewer Goal
|
|
18
|
+
|
|
19
|
+
Do not only ask whether tests exist. Judge whether the tests are strong enough to trust the implementation.
|
|
20
|
+
|
|
21
|
+
## Required Review Flow
|
|
22
|
+
|
|
23
|
+
1. Verify test files exist for changed production code.
|
|
24
|
+
2. Verify tests pass with the repo-native test command.
|
|
25
|
+
3. Verify lint/type expectations relevant to tests still pass.
|
|
26
|
+
4. Inspect the changed source and the corresponding tests.
|
|
27
|
+
5. Evaluate test quality across these dimensions:
|
|
28
|
+
- coverage quality
|
|
29
|
+
- assertion specificity
|
|
30
|
+
- Arrange / Act / Assert structure
|
|
31
|
+
- mock hygiene
|
|
32
|
+
- naming quality
|
|
33
|
+
- async correctness
|
|
34
|
+
- fixture/data quality
|
|
35
|
+
6. Decide whether the test suite is acceptable, acceptable with recommendations, or needs Builder rework.
|
|
36
|
+
|
|
37
|
+
## Automatic Rework Triggers
|
|
38
|
+
|
|
39
|
+
Send the feature back to Builder when any of these are true:
|
|
40
|
+
|
|
41
|
+
- changed production code has no meaningful unit tests
|
|
42
|
+
- tests are failing
|
|
43
|
+
- coverage is below the project hard gate
|
|
44
|
+
- assertions are mostly vague or vacuous
|
|
45
|
+
- tests rely on implementation details instead of behavior
|
|
46
|
+
- mocks bleed between tests or mock the unit under test
|
|
47
|
+
- async behavior is unawaited, flaky, or only partially covered
|
|
48
|
+
|
|
49
|
+
## Reviewer Output Expectations
|
|
50
|
+
|
|
51
|
+
- Cite concrete evidence: file paths, line numbers, and command output.
|
|
52
|
+
- Separate blocking issues from non-blocking recommendations.
|
|
53
|
+
- If test quality is insufficient, record `needs_rework` and send the workflow back to Builder.
|
|
54
|
+
- If tests are acceptable, still capture recommendations for the next iteration.
|
|
55
|
+
|
|
56
|
+
## Minimal Scoring Rubric
|
|
57
|
+
|
|
58
|
+
- `PASS`: tests are trustworthy and compliant
|
|
59
|
+
- `PASS_WITH_RECOMMENDATIONS`: acceptable now, but improvements are still warranted
|
|
60
|
+
- `NEEDS_REWORK`: Builder must fix tests before integration
|
|
61
|
+
- `ESCALATE`: test evidence reveals a deeper production bug or ambiguous requirement
|
|
62
|
+
|
|
63
|
+
## Reviewer Checklist
|
|
64
|
+
|
|
65
|
+
- [ ] Test files present for changed code
|
|
66
|
+
- [ ] Tests pass
|
|
67
|
+
- [ ] Coverage meets hard gate
|
|
68
|
+
- [ ] Assertions are specific
|
|
69
|
+
- [ ] Mock cleanup is correct
|
|
70
|
+
- [ ] Async paths are handled correctly
|
|
71
|
+
- [ ] Result is either approved or routed back to Builder with precise feedback
|
|
72
|
+
|
|
73
|
+
Use this skill alongside security review. Weak tests are a workflow-quality issue and should block progression just like important review findings.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: unit-tests-sdd
|
|
3
|
+
description: Mandatory unit test generation protocol for Builder. Enforces guardrails, coverage gates, assertion quality, and escalation rules so code changes always ship with meaningful tests.
|
|
4
|
+
category: testing
|
|
5
|
+
compatible_with:
|
|
6
|
+
- vitest
|
|
7
|
+
- jest
|
|
8
|
+
- react-patterns
|
|
9
|
+
- nextjs-dev
|
|
10
|
+
- nodejs-fastify
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Unit Test Generation Protocol
|
|
14
|
+
|
|
15
|
+
Use this skill whenever Builder changes production code. Tests are not optional.
|
|
16
|
+
|
|
17
|
+
## Required Outcome
|
|
18
|
+
|
|
19
|
+
- Add or update unit tests for every code-changing task.
|
|
20
|
+
- Cover acceptance criteria, edge cases, happy paths, and failure paths.
|
|
21
|
+
- Run the repo-native test command and lint/type checks relevant to tests.
|
|
22
|
+
- Stop and escalate if tests cannot be made green without changing production code in ways the spec did not authorize.
|
|
23
|
+
|
|
24
|
+
## Hard Guardrails
|
|
25
|
+
|
|
26
|
+
- Never skip tests for code changes.
|
|
27
|
+
- Never modify production code only to make a bad test pass.
|
|
28
|
+
- Never delete or weaken an existing passing test just to get green.
|
|
29
|
+
- Never use `any`, `@ts-ignore`, or `@ts-expect-error` as shortcuts in tests.
|
|
30
|
+
- Never use vacuous assertions like `expect(true).toBe(true)`.
|
|
31
|
+
- Never rely on snapshots as the main proof of behavior.
|
|
32
|
+
- Never mock the module under test; mock dependencies only.
|
|
33
|
+
- Never leave `.only`, `.skip`, `console.log`, `debugger`, or sleep-style delays in committed tests.
|
|
34
|
+
|
|
35
|
+
## Build Workflow
|
|
36
|
+
|
|
37
|
+
1. Read the source file fully before writing tests.
|
|
38
|
+
2. Identify exports, branches, async behavior, and error paths.
|
|
39
|
+
3. Add or update colocated test files following repo conventions.
|
|
40
|
+
4. Use clear `describe` / `it` names tied to observable behavior.
|
|
41
|
+
5. Follow Arrange / Act / Assert in each non-trivial test.
|
|
42
|
+
6. Run the repo-native test command with coverage.
|
|
43
|
+
7. Run the repo-native lint command if tests or linted files changed.
|
|
44
|
+
8. If coverage is below the project hard gate, add tests or escalate.
|
|
45
|
+
|
|
46
|
+
## Coverage Expectations
|
|
47
|
+
|
|
48
|
+
- Meet the project's hard coverage gates at minimum.
|
|
49
|
+
- Aim for near-complete coverage on the changed unit.
|
|
50
|
+
- Cover both success and failure branches.
|
|
51
|
+
- Document genuine coverage gaps explicitly; do not hide them with meaningless tests.
|
|
52
|
+
|
|
53
|
+
## Assertion Rules
|
|
54
|
+
|
|
55
|
+
- Prefer exact assertions over truthy/falsy checks.
|
|
56
|
+
- Assert outputs, visible UI, callback args, and thrown/rejected errors.
|
|
57
|
+
- Avoid implementation-detail assertions on private state or internals.
|
|
58
|
+
- For mocks, prefer argument assertions such as `toHaveBeenCalledWith(...)`.
|
|
59
|
+
|
|
60
|
+
## Async Rules
|
|
61
|
+
|
|
62
|
+
- Make async tests `async` and await real async work.
|
|
63
|
+
- Test both resolved and rejected branches.
|
|
64
|
+
- Use framework-native async helpers (`waitFor`, fake timers, `act`) instead of arbitrary delays.
|
|
65
|
+
- Restore timers and mocks in teardown.
|
|
66
|
+
|
|
67
|
+
## Escalate Immediately When
|
|
68
|
+
|
|
69
|
+
- A correct test exposes a production bug.
|
|
70
|
+
- The test cannot pass without unauthorized production-code changes.
|
|
71
|
+
- Coverage gates remain below threshold after good-faith testing.
|
|
72
|
+
- The mocking strategy is ambiguous or would violate project guardrails.
|
|
73
|
+
|
|
74
|
+
## Builder Checklist
|
|
75
|
+
|
|
76
|
+
- [ ] Tests added or updated for the change
|
|
77
|
+
- [ ] Acceptance criteria covered
|
|
78
|
+
- [ ] Error paths covered
|
|
79
|
+
- [ ] Coverage checked
|
|
80
|
+
- [ ] Test command passed
|
|
81
|
+
- [ ] Lint/type checks relevant to tests passed
|
|
82
|
+
|
|
83
|
+
Pair this skill with the repo's detected test framework skill such as `vitest` or `jest`.
|