agentic-team-templates 0.9.2 → 0.11.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.
@@ -0,0 +1,403 @@
1
+ # JavaScript Testing
2
+
3
+ Expert-level testing patterns across all JavaScript environments.
4
+
5
+ ## Testing Philosophy
6
+
7
+ - Tests are production code — same quality standards apply
8
+ - Test behavior and contracts, never implementation details
9
+ - Every bug fix starts with a failing test that reproduces the bug
10
+ - If it's hard to test, the design is wrong — refactor the code, not the test
11
+
12
+ ## Test Structure
13
+
14
+ ### Arrange-Act-Assert
15
+
16
+ ```typescript
17
+ describe('UserService', () => {
18
+ it('creates a user with hashed password', async () => {
19
+ // Arrange
20
+ const input = { email: 'test@example.com', password: 'secret123' };
21
+ const repo = createMockRepo();
22
+
23
+ // Act
24
+ const user = await createUser(repo, input);
25
+
26
+ // Assert
27
+ expect(user.email).toBe('test@example.com');
28
+ expect(user.passwordHash).not.toBe('secret123');
29
+ expect(await verify(user.passwordHash, 'secret123')).toBe(true);
30
+ });
31
+ });
32
+ ```
33
+
34
+ ### Descriptive Test Names
35
+
36
+ ```typescript
37
+ // Tests should read like specifications
38
+ describe('parseDate', () => {
39
+ it('parses ISO 8601 strings into Date objects', () => { ... });
40
+ it('returns null for invalid date strings', () => { ... });
41
+ it('handles timezone offsets correctly', () => { ... });
42
+ it('treats dates without timezone as UTC', () => { ... });
43
+ });
44
+
45
+ // Not:
46
+ it('works', () => { ... });
47
+ it('test 1', () => { ... });
48
+ it('should work correctly', () => { ... });
49
+ ```
50
+
51
+ ## Unit Testing
52
+
53
+ ### Pure Functions
54
+
55
+ ```typescript
56
+ import { describe, it, expect } from 'vitest';
57
+
58
+ // Exhaustive edge case coverage
59
+ describe('clamp', () => {
60
+ it('returns value when within range', () => {
61
+ expect(clamp(5, 0, 10)).toBe(5);
62
+ });
63
+
64
+ it('returns min when value is below range', () => {
65
+ expect(clamp(-5, 0, 10)).toBe(0);
66
+ });
67
+
68
+ it('returns max when value is above range', () => {
69
+ expect(clamp(15, 0, 10)).toBe(10);
70
+ });
71
+
72
+ it('handles min equal to max', () => {
73
+ expect(clamp(5, 3, 3)).toBe(3);
74
+ });
75
+
76
+ it('handles negative ranges', () => {
77
+ expect(clamp(0, -10, -5)).toBe(-5);
78
+ });
79
+ });
80
+ ```
81
+
82
+ ### Parameterized Tests
83
+
84
+ ```typescript
85
+ // Use it.each for data-driven tests
86
+ describe('slugify', () => {
87
+ it.each([
88
+ ['Hello World', 'hello-world'],
89
+ [' spaces ', 'spaces'],
90
+ ['Special!@#Characters', 'specialcharacters'],
91
+ ['already-slugified', 'already-slugified'],
92
+ ['MiXeD CaSe', 'mixed-case'],
93
+ ['', ''],
94
+ ])('converts "%s" to "%s"', (input, expected) => {
95
+ expect(slugify(input)).toBe(expected);
96
+ });
97
+ });
98
+ ```
99
+
100
+ ### Testing Async Code
101
+
102
+ ```typescript
103
+ describe('fetchUser', () => {
104
+ it('returns user data for valid ID', async () => {
105
+ const user = await fetchUser('123');
106
+ expect(user).toEqual({ id: '123', name: 'John' });
107
+ });
108
+
109
+ it('returns error result for missing user', async () => {
110
+ const result = await fetchUser('nonexistent');
111
+ expect(result.ok).toBe(false);
112
+ expect(result.error.code).toBe('NOT_FOUND');
113
+ });
114
+
115
+ it('respects AbortSignal', async () => {
116
+ const controller = new AbortController();
117
+ controller.abort();
118
+
119
+ await expect(
120
+ fetchUser('123', { signal: controller.signal })
121
+ ).rejects.toThrow('aborted');
122
+ });
123
+ });
124
+ ```
125
+
126
+ ## Integration Testing
127
+
128
+ ### API Testing
129
+
130
+ ```typescript
131
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
132
+ import { createApp } from '../app.js';
133
+
134
+ describe('POST /api/users', () => {
135
+ let app: ReturnType<typeof createApp>;
136
+
137
+ beforeAll(async () => {
138
+ app = createApp({ db: createTestDb() });
139
+ await app.ready();
140
+ });
141
+
142
+ afterAll(async () => {
143
+ await app.close();
144
+ });
145
+
146
+ it('creates user and returns 201', async () => {
147
+ const response = await app.inject({
148
+ method: 'POST',
149
+ url: '/api/users',
150
+ payload: { email: 'new@example.com', name: 'New User' },
151
+ });
152
+
153
+ expect(response.statusCode).toBe(201);
154
+ expect(response.json()).toMatchObject({
155
+ email: 'new@example.com',
156
+ name: 'New User',
157
+ });
158
+ });
159
+
160
+ it('returns 400 for invalid email', async () => {
161
+ const response = await app.inject({
162
+ method: 'POST',
163
+ url: '/api/users',
164
+ payload: { email: 'not-an-email', name: 'Bad' },
165
+ });
166
+
167
+ expect(response.statusCode).toBe(400);
168
+ });
169
+
170
+ it('returns 409 for duplicate email', async () => {
171
+ await app.inject({
172
+ method: 'POST',
173
+ url: '/api/users',
174
+ payload: { email: 'dupe@example.com', name: 'First' },
175
+ });
176
+
177
+ const response = await app.inject({
178
+ method: 'POST',
179
+ url: '/api/users',
180
+ payload: { email: 'dupe@example.com', name: 'Second' },
181
+ });
182
+
183
+ expect(response.statusCode).toBe(409);
184
+ });
185
+ });
186
+ ```
187
+
188
+ ### Database Testing
189
+
190
+ ```typescript
191
+ // Use transactions for test isolation
192
+ describe('UserRepository', () => {
193
+ let db: TestDatabase;
194
+
195
+ beforeEach(async () => {
196
+ db = await createTestDatabase();
197
+ await db.beginTransaction();
198
+ });
199
+
200
+ afterEach(async () => {
201
+ await db.rollbackTransaction();
202
+ await db.close();
203
+ });
204
+
205
+ it('finds user by email', async () => {
206
+ await db.seed({ users: [{ email: 'test@example.com' }] });
207
+ const repo = new UserRepository(db);
208
+
209
+ const user = await repo.findByEmail('test@example.com');
210
+
211
+ expect(user).not.toBeNull();
212
+ expect(user!.email).toBe('test@example.com');
213
+ });
214
+ });
215
+ ```
216
+
217
+ ## React Component Testing
218
+
219
+ ```tsx
220
+ import { render, screen, within } from '@testing-library/react';
221
+ import userEvent from '@testing-library/user-event';
222
+ import { vi, describe, it, expect } from 'vitest';
223
+
224
+ describe('TodoList', () => {
225
+ it('adds a new todo when form is submitted', async () => {
226
+ const user = userEvent.setup();
227
+ render(<TodoList />);
228
+
229
+ await user.type(screen.getByRole('textbox', { name: /new todo/i }), 'Buy milk');
230
+ await user.click(screen.getByRole('button', { name: /add/i }));
231
+
232
+ expect(screen.getByText('Buy milk')).toBeInTheDocument();
233
+ });
234
+
235
+ it('marks todo as complete when checkbox is clicked', async () => {
236
+ const user = userEvent.setup();
237
+ render(<TodoList initialTodos={[{ id: '1', text: 'Test', done: false }]} />);
238
+
239
+ await user.click(screen.getByRole('checkbox', { name: /test/i }));
240
+
241
+ expect(screen.getByRole('checkbox', { name: /test/i })).toBeChecked();
242
+ });
243
+
244
+ it('filters completed todos', async () => {
245
+ const user = userEvent.setup();
246
+ render(
247
+ <TodoList initialTodos={[
248
+ { id: '1', text: 'Done', done: true },
249
+ { id: '2', text: 'Not done', done: false },
250
+ ]} />
251
+ );
252
+
253
+ await user.click(screen.getByRole('button', { name: /active/i }));
254
+
255
+ expect(screen.queryByText('Done')).not.toBeInTheDocument();
256
+ expect(screen.getByText('Not done')).toBeInTheDocument();
257
+ });
258
+ });
259
+ ```
260
+
261
+ ## Mocking
262
+
263
+ ### Strategic Mocking
264
+
265
+ ```typescript
266
+ // Mock at boundaries, not internals
267
+ // Good: Mock the HTTP client, not internal functions
268
+ const mockFetch = vi.fn();
269
+
270
+ // Bad: Mocking private methods or internal state
271
+ // If you need to mock internals, the design needs work
272
+
273
+ // Use dependency injection for testability
274
+ interface EmailSender {
275
+ send(to: string, subject: string, body: string): Promise<void>;
276
+ }
277
+
278
+ // Production
279
+ const smtpSender: EmailSender = { send: async (...args) => { /* SMTP */ } };
280
+
281
+ // Test
282
+ const mockSender: EmailSender = { send: vi.fn() };
283
+
284
+ const service = new NotificationService(mockSender);
285
+ await service.notifyUser(user, 'Welcome');
286
+ expect(mockSender.send).toHaveBeenCalledWith(user.email, 'Welcome', expect.any(String));
287
+ ```
288
+
289
+ ### MSW for API Mocking
290
+
291
+ ```typescript
292
+ import { http, HttpResponse } from 'msw';
293
+ import { setupServer } from 'msw/node';
294
+
295
+ const handlers = [
296
+ http.get('/api/users/:id', ({ params }) => {
297
+ return HttpResponse.json({ id: params.id, name: 'Test User' });
298
+ }),
299
+ http.post('/api/users', async ({ request }) => {
300
+ const body = await request.json();
301
+ return HttpResponse.json(body, { status: 201 });
302
+ }),
303
+ ];
304
+
305
+ const server = setupServer(...handlers);
306
+ beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
307
+ afterEach(() => server.resetHandlers());
308
+ afterAll(() => server.close());
309
+
310
+ // Override for specific test
311
+ it('handles server errors', async () => {
312
+ server.use(
313
+ http.get('/api/users/:id', () => {
314
+ return new HttpResponse(null, { status: 500 });
315
+ }),
316
+ );
317
+
318
+ const result = await fetchUser('123');
319
+ expect(result.ok).toBe(false);
320
+ });
321
+ ```
322
+
323
+ ## E2E Testing
324
+
325
+ ```typescript
326
+ import { test, expect } from '@playwright/test';
327
+
328
+ test.describe('Authentication flow', () => {
329
+ test('login, perform action, logout', async ({ page }) => {
330
+ await page.goto('/login');
331
+
332
+ await page.getByLabel('Email').fill('user@example.com');
333
+ await page.getByLabel('Password').fill('password');
334
+ await page.getByRole('button', { name: 'Sign in' }).click();
335
+
336
+ await expect(page).toHaveURL('/dashboard');
337
+ await expect(page.getByText('Welcome')).toBeVisible();
338
+
339
+ await page.getByRole('button', { name: 'Sign out' }).click();
340
+ await expect(page).toHaveURL('/login');
341
+ });
342
+ });
343
+
344
+ // Visual regression
345
+ test('homepage matches snapshot', async ({ page }) => {
346
+ await page.goto('/');
347
+ await expect(page).toHaveScreenshot('homepage.png', {
348
+ maxDiffPixelRatio: 0.01,
349
+ });
350
+ });
351
+ ```
352
+
353
+ ## Test Anti-Patterns
354
+
355
+ ```typescript
356
+ // Never: Testing implementation details
357
+ expect(component.instance().state.isLoading).toBe(true); // BAD
358
+
359
+ // Never: Snapshot testing for logic (only for visual regression)
360
+ expect(complexObject).toMatchSnapshot(); // BAD — fragile, meaningless diffs
361
+
362
+ // Never: Sleeping instead of waiting
363
+ await new Promise(r => setTimeout(r, 1000)); // BAD
364
+ await waitFor(() => expect(screen.getByText('loaded')).toBeVisible()); // GOOD
365
+
366
+ // Never: Tests that depend on execution order
367
+ // Each test must be independently runnable
368
+
369
+ // Never: Ignoring flaky tests
370
+ // A flaky test is a bug — either in the test or the code. Fix it.
371
+
372
+ // Never: Testing third-party library behavior
373
+ // Trust your dependencies. Test YOUR code's integration with them.
374
+ ```
375
+
376
+ ## Coverage
377
+
378
+ ```typescript
379
+ // vitest.config.ts
380
+ export default defineConfig({
381
+ test: {
382
+ coverage: {
383
+ provider: 'v8',
384
+ thresholds: {
385
+ statements: 80,
386
+ branches: 80,
387
+ functions: 80,
388
+ lines: 80,
389
+ },
390
+ exclude: [
391
+ 'node_modules/',
392
+ 'test/',
393
+ '**/*.d.ts',
394
+ '**/*.config.*',
395
+ ],
396
+ },
397
+ },
398
+ });
399
+
400
+ // Coverage is a metric, not a goal
401
+ // 100% coverage with bad tests is worse than 70% coverage with great tests
402
+ // Focus on testing behavior and edge cases, not hitting line counts
403
+ ```
@@ -0,0 +1,176 @@
1
+ # JavaScript Tooling
2
+
3
+ Modern JavaScript tooling configuration and best practices.
4
+
5
+ ## TypeScript Configuration
6
+
7
+ ```jsonc
8
+ // tsconfig.json — strict everything
9
+ {
10
+ "compilerOptions": {
11
+ "strict": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "noUnusedLocals": true,
14
+ "noUnusedParameters": true,
15
+ "exactOptionalPropertyTypes": true,
16
+ "noImplicitReturns": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ "isolatedModules": true,
20
+ "verbatimModuleSyntax": true,
21
+ "moduleResolution": "bundler",
22
+ "module": "ESNext",
23
+ "target": "ES2022",
24
+ "lib": ["ES2023"],
25
+ "skipLibCheck": true,
26
+ "declaration": true,
27
+ "declarationMap": true,
28
+ "sourceMap": true
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Package Management
34
+
35
+ ```jsonc
36
+ // package.json essentials
37
+ {
38
+ "type": "module",
39
+ "engines": { "node": ">=20" },
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "import": "./dist/index.js"
44
+ }
45
+ },
46
+ "files": ["dist"],
47
+ "sideEffects": false
48
+ }
49
+ ```
50
+
51
+ ### Dependency Hygiene
52
+
53
+ - Pin exact versions for applications (`"react": "19.0.0"`)
54
+ - Use ranges for libraries (`"react": "^19.0.0"`)
55
+ - Audit dependencies regularly (`npm audit`)
56
+ - Prefer packages with zero dependencies
57
+ - Check bundle size impact before adding (`bundlephobia.com`)
58
+ - Remove unused dependencies (`npx depcheck`)
59
+
60
+ ## Linting
61
+
62
+ ```javascript
63
+ // eslint.config.js (flat config)
64
+ import tseslint from 'typescript-eslint';
65
+
66
+ export default tseslint.config(
67
+ ...tseslint.configs.strictTypeChecked,
68
+ {
69
+ rules: {
70
+ '@typescript-eslint/no-explicit-any': 'error',
71
+ '@typescript-eslint/no-floating-promises': 'error',
72
+ '@typescript-eslint/no-misused-promises': 'error',
73
+ '@typescript-eslint/prefer-readonly': 'error',
74
+ '@typescript-eslint/strict-boolean-expressions': 'error',
75
+ 'no-console': ['warn', { allow: ['warn', 'error'] }],
76
+ },
77
+ },
78
+ );
79
+ ```
80
+
81
+ ## Build Tools
82
+
83
+ ### Vite (Frontend)
84
+
85
+ ```typescript
86
+ // vite.config.ts
87
+ import { defineConfig } from 'vite';
88
+
89
+ export default defineConfig({
90
+ build: {
91
+ target: 'es2022',
92
+ sourcemap: true,
93
+ rollupOptions: {
94
+ output: {
95
+ manualChunks: {
96
+ vendor: ['react', 'react-dom'],
97
+ },
98
+ },
99
+ },
100
+ },
101
+ });
102
+ ```
103
+
104
+ ### tsup (Libraries)
105
+
106
+ ```typescript
107
+ // tsup.config.ts
108
+ import { defineConfig } from 'tsup';
109
+
110
+ export default defineConfig({
111
+ entry: ['src/index.ts'],
112
+ format: ['esm'],
113
+ dts: true,
114
+ sourcemap: true,
115
+ clean: true,
116
+ target: 'es2022',
117
+ splitting: true,
118
+ treeshake: true,
119
+ });
120
+ ```
121
+
122
+ ## Git Hooks
123
+
124
+ ```jsonc
125
+ // package.json
126
+ {
127
+ "scripts": {
128
+ "prepare": "husky",
129
+ "lint": "eslint .",
130
+ "typecheck": "tsc --noEmit",
131
+ "test": "vitest run",
132
+ "check": "npm run typecheck && npm run lint && npm run test"
133
+ },
134
+ "lint-staged": {
135
+ "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
136
+ "*.{json,md,yml}": ["prettier --write"]
137
+ }
138
+ }
139
+ ```
140
+
141
+ ## Debugging
142
+
143
+ ```typescript
144
+ // Node.js debugger
145
+ // node --inspect-brk app.js
146
+ // Then connect Chrome DevTools to chrome://inspect
147
+
148
+ // Conditional breakpoints in code
149
+ if (suspiciousValue > threshold) {
150
+ debugger; // Only hits when condition is true
151
+ }
152
+
153
+ // Structured logging over console.log
154
+ import { createLogger } from './logger.js';
155
+ const log = createLogger('user-service');
156
+
157
+ log.info('User created', { userId: user.id, email: user.email });
158
+ log.error('Failed to create user', { error, input });
159
+ // Never log passwords, tokens, or PII
160
+ ```
161
+
162
+ ## Editor Configuration
163
+
164
+ ```jsonc
165
+ // .vscode/settings.json (also works in Cursor)
166
+ {
167
+ "editor.formatOnSave": true,
168
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
169
+ "editor.codeActionsOnSave": {
170
+ "source.fixAll.eslint": "explicit",
171
+ "source.organizeImports": "explicit"
172
+ },
173
+ "typescript.preferences.importModuleSpecifierEnding": "js",
174
+ "typescript.tsdk": "node_modules/typescript/lib"
175
+ }
176
+ ```