@plazmodium/odin 0.3.3-beta → 0.3.5-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.
Files changed (133) hide show
  1. package/README.md +25 -10
  2. package/builtin/ODIN.md +1067 -0
  3. package/builtin/agent-definitions/README.md +170 -0
  4. package/builtin/agent-definitions/_shared-context.md +377 -0
  5. package/builtin/agent-definitions/architect.md +627 -0
  6. package/builtin/agent-definitions/builder.md +713 -0
  7. package/builtin/agent-definitions/discovery.md +293 -0
  8. package/builtin/agent-definitions/documenter.md +238 -0
  9. package/builtin/agent-definitions/guardian.md +1049 -0
  10. package/builtin/agent-definitions/integrator.md +189 -0
  11. package/builtin/agent-definitions/planning.md +236 -0
  12. package/builtin/agent-definitions/product.md +405 -0
  13. package/builtin/agent-definitions/release.md +205 -0
  14. package/builtin/agent-definitions/reviewer.md +447 -0
  15. package/builtin/agent-definitions/watcher.md +402 -0
  16. package/builtin/skills/api/graphql/SKILL.md +548 -0
  17. package/builtin/skills/api/grpc/SKILL.md +554 -0
  18. package/builtin/skills/api/rest-api/SKILL.md +469 -0
  19. package/builtin/skills/api/trpc/SKILL.md +503 -0
  20. package/builtin/skills/architecture/clean-architecture/SKILL.md +141 -0
  21. package/builtin/skills/architecture/domain-driven-design/SKILL.md +129 -0
  22. package/builtin/skills/architecture/event-driven/SKILL.md +145 -0
  23. package/builtin/skills/architecture/microservices/SKILL.md +143 -0
  24. package/builtin/skills/architecture/tla-precheck/SKILL.md +171 -0
  25. package/builtin/skills/backend/golang-gin/SKILL.md +141 -0
  26. package/builtin/skills/backend/nodejs-express/SKILL.md +277 -0
  27. package/builtin/skills/backend/nodejs-fastify/SKILL.md +152 -0
  28. package/builtin/skills/backend/python-django/SKILL.md +128 -0
  29. package/builtin/skills/backend/python-fastapi/SKILL.md +140 -0
  30. package/builtin/skills/database/mongodb/SKILL.md +132 -0
  31. package/builtin/skills/database/postgresql/SKILL.md +120 -0
  32. package/builtin/skills/database/prisma-orm/SKILL.md +366 -0
  33. package/builtin/skills/database/redis/SKILL.md +140 -0
  34. package/builtin/skills/database/supabase/SKILL.md +416 -0
  35. package/builtin/skills/devops/aws/SKILL.md +382 -0
  36. package/builtin/skills/devops/docker/SKILL.md +359 -0
  37. package/builtin/skills/devops/github-actions/SKILL.md +435 -0
  38. package/builtin/skills/devops/kubernetes/SKILL.md +459 -0
  39. package/builtin/skills/devops/terraform/SKILL.md +453 -0
  40. package/builtin/skills/frontend/alpine-dev/SKILL.md +27 -0
  41. package/builtin/skills/frontend/angular-dev/SKILL.md +28 -0
  42. package/builtin/skills/frontend/astro-dev/SKILL.md +28 -0
  43. package/builtin/skills/frontend/htmx-dev/SKILL.md +28 -0
  44. package/builtin/skills/frontend/nextjs-dev/SKILL.md +470 -0
  45. package/builtin/skills/frontend/react-patterns/SKILL.md +166 -0
  46. package/builtin/skills/frontend/svelte-dev/SKILL.md +28 -0
  47. package/builtin/skills/frontend/tailwindcss/SKILL.md +131 -0
  48. package/builtin/skills/frontend/vuejs-dev/SKILL.md +28 -0
  49. package/builtin/skills/generic-dev/SKILL.md +307 -0
  50. package/builtin/skills/testing/cypress/SKILL.md +372 -0
  51. package/builtin/skills/testing/jest/SKILL.md +176 -0
  52. package/builtin/skills/testing/playwright/SKILL.md +341 -0
  53. package/builtin/skills/testing/unit-tests-eval-sdd/SKILL.md +73 -0
  54. package/builtin/skills/testing/unit-tests-sdd/SKILL.md +83 -0
  55. package/builtin/skills/testing/vitest/SKILL.md +249 -0
  56. package/dist/adapters/skills/filesystem.d.ts +1 -0
  57. package/dist/adapters/skills/filesystem.d.ts.map +1 -1
  58. package/dist/adapters/skills/filesystem.js +6 -18
  59. package/dist/adapters/skills/filesystem.js.map +1 -1
  60. package/dist/adapters/skills/types.d.ts +1 -0
  61. package/dist/adapters/skills/types.d.ts.map +1 -1
  62. package/dist/adapters/workflow-state/in-memory.d.ts +10 -2
  63. package/dist/adapters/workflow-state/in-memory.d.ts.map +1 -1
  64. package/dist/adapters/workflow-state/in-memory.js +98 -5
  65. package/dist/adapters/workflow-state/in-memory.js.map +1 -1
  66. package/dist/adapters/workflow-state/supabase.d.ts +8 -2
  67. package/dist/adapters/workflow-state/supabase.d.ts.map +1 -1
  68. package/dist/adapters/workflow-state/supabase.js +204 -0
  69. package/dist/adapters/workflow-state/supabase.js.map +1 -1
  70. package/dist/adapters/workflow-state/types.d.ts +15 -1
  71. package/dist/adapters/workflow-state/types.d.ts.map +1 -1
  72. package/dist/builtin-assets.d.ts +8 -0
  73. package/dist/builtin-assets.d.ts.map +1 -0
  74. package/dist/builtin-assets.js +90 -0
  75. package/dist/builtin-assets.js.map +1 -0
  76. package/dist/domain/skill-draft-validation.d.ts +18 -0
  77. package/dist/domain/skill-draft-validation.d.ts.map +1 -0
  78. package/dist/domain/skill-draft-validation.js +100 -0
  79. package/dist/domain/skill-draft-validation.js.map +1 -0
  80. package/dist/domain/skill-proposals.d.ts +11 -0
  81. package/dist/domain/skill-proposals.d.ts.map +1 -0
  82. package/dist/domain/skill-proposals.js +103 -0
  83. package/dist/domain/skill-proposals.js.map +1 -0
  84. package/dist/init.js +69 -11
  85. package/dist/init.js.map +1 -1
  86. package/dist/schemas.d.ts +39 -1
  87. package/dist/schemas.d.ts.map +1 -1
  88. package/dist/schemas.js +30 -1
  89. package/dist/schemas.js.map +1 -1
  90. package/dist/server.js +38 -2
  91. package/dist/server.js.map +1 -1
  92. package/dist/tools/apply-migrations.d.ts +10 -0
  93. package/dist/tools/apply-migrations.d.ts.map +1 -1
  94. package/dist/tools/apply-migrations.js +10 -26
  95. package/dist/tools/apply-migrations.js.map +1 -1
  96. package/dist/tools/capture-learning.d.ts.map +1 -1
  97. package/dist/tools/capture-learning.js +14 -1
  98. package/dist/tools/capture-learning.js.map +1 -1
  99. package/dist/tools/get-skill-proposal-queue.d.ts +5 -0
  100. package/dist/tools/get-skill-proposal-queue.d.ts.map +1 -0
  101. package/dist/tools/get-skill-proposal-queue.js +21 -0
  102. package/dist/tools/get-skill-proposal-queue.js.map +1 -0
  103. package/dist/tools/get-skill-proposals.d.ts +4 -0
  104. package/dist/tools/get-skill-proposals.d.ts.map +1 -0
  105. package/dist/tools/get-skill-proposals.js +11 -0
  106. package/dist/tools/get-skill-proposals.js.map +1 -0
  107. package/dist/tools/prepare-phase-context.d.ts.map +1 -1
  108. package/dist/tools/prepare-phase-context.js +5 -0
  109. package/dist/tools/prepare-phase-context.js.map +1 -1
  110. package/dist/tools/publish-skill-proposal.d.ts +5 -0
  111. package/dist/tools/publish-skill-proposal.d.ts.map +1 -0
  112. package/dist/tools/publish-skill-proposal.js +57 -0
  113. package/dist/tools/publish-skill-proposal.js.map +1 -0
  114. package/dist/tools/record-skill-proposal-decision.d.ts +4 -0
  115. package/dist/tools/record-skill-proposal-decision.d.ts.map +1 -0
  116. package/dist/tools/record-skill-proposal-decision.js +22 -0
  117. package/dist/tools/record-skill-proposal-decision.js.map +1 -0
  118. package/dist/tools/record-skill-proposal-draft.d.ts +5 -0
  119. package/dist/tools/record-skill-proposal-draft.d.ts.map +1 -0
  120. package/dist/tools/record-skill-proposal-draft.js +65 -0
  121. package/dist/tools/record-skill-proposal-draft.js.map +1 -0
  122. package/dist/tools/sync-skill-proposal-candidates.d.ts +5 -0
  123. package/dist/tools/sync-skill-proposal-candidates.d.ts.map +1 -0
  124. package/dist/tools/sync-skill-proposal-candidates.js +20 -0
  125. package/dist/tools/sync-skill-proposal-candidates.js.map +1 -0
  126. package/dist/types.d.ts +41 -0
  127. package/dist/types.d.ts.map +1 -1
  128. package/dist/types.js +2 -0
  129. package/dist/types.js.map +1 -1
  130. package/migrations/009_skill_proposal_candidates.sql +124 -0
  131. package/migrations/010_skill_proposals.sql +36 -0
  132. package/migrations/README.md +6 -0
  133. package/package.json +5 -3
@@ -0,0 +1,372 @@
1
+ ---
2
+ name: cypress
3
+ description: Cypress end-to-end testing expertise for web applications. Covers browser testing, component testing, network stubbing, and CI integration. Use for E2E and component testing with real-time browser preview and time-travel debugging.
4
+ category: testing
5
+ compatible_with:
6
+ - nextjs-dev
7
+ - react-patterns
8
+ - vuejs-dev
9
+ - github-actions
10
+ ---
11
+
12
+ # Cypress End-to-End Testing
13
+
14
+ ## Instructions
15
+
16
+ 1. **Assess the testing scope**: Determine if it's E2E tests, component tests, or API tests.
17
+ 2. **Follow Cypress conventions**:
18
+ - E2E tests: `cypress/e2e/*.cy.ts`
19
+ - Component tests: `*.cy.tsx` alongside components
20
+ - Use `cy` commands for all interactions
21
+ 3. **Provide complete examples**: Include selectors, assertions, and custom commands.
22
+ 4. **Guide on best practices**: Selectors, waiting, anti-patterns.
23
+ 5. **Cover advanced features**: Intercepts, fixtures, custom commands.
24
+
25
+ ## Test Structure
26
+
27
+ ```typescript
28
+ describe('User Authentication', () => {
29
+ beforeEach(() => {
30
+ cy.visit('/login');
31
+ });
32
+
33
+ it('should login with valid credentials', () => {
34
+ cy.get('[data-testid="email"]').type('user@example.com');
35
+ cy.get('[data-testid="password"]').type('password123');
36
+ cy.get('[data-testid="submit"]').click();
37
+
38
+ cy.url().should('include', '/dashboard');
39
+ cy.get('h1').should('contain', 'Welcome');
40
+ });
41
+
42
+ it('should show error for invalid credentials', () => {
43
+ cy.get('[data-testid="email"]').type('invalid@example.com');
44
+ cy.get('[data-testid="password"]').type('wrong');
45
+ cy.get('[data-testid="submit"]').click();
46
+
47
+ cy.contains('Invalid credentials').should('be.visible');
48
+ });
49
+ });
50
+ ```
51
+
52
+ ## Selectors (Recommended Order)
53
+
54
+ ```typescript
55
+ // 1. Data attributes (most resilient)
56
+ cy.get('[data-testid="submit-button"]');
57
+ cy.get('[data-cy="email-input"]');
58
+
59
+ // 2. Contains text
60
+ cy.contains('Submit');
61
+ cy.contains('button', 'Submit'); // Element + text
62
+
63
+ // 3. Role-based (with cypress-testing-library)
64
+ cy.findByRole('button', { name: 'Submit' });
65
+ cy.findByLabelText('Email');
66
+ cy.findByPlaceholderText('Enter email');
67
+
68
+ // 4. CSS selectors
69
+ cy.get('.submit-btn');
70
+ cy.get('#email-input');
71
+ cy.get('form input[type="email"]');
72
+ ```
73
+
74
+ ## Common Commands
75
+
76
+ ```typescript
77
+ // Navigation
78
+ cy.visit('/page');
79
+ cy.go('back');
80
+ cy.reload();
81
+
82
+ // Clicking
83
+ cy.get('button').click();
84
+ cy.get('button').dblclick();
85
+ cy.get('button').rightclick();
86
+ cy.get('button').click({ force: true }); // Skip visibility checks
87
+
88
+ // Typing
89
+ cy.get('input').type('Hello');
90
+ cy.get('input').type('{enter}'); // Special keys
91
+ cy.get('input').type('text{enter}'); // Text + enter
92
+ cy.get('input').clear().type('new text');
93
+
94
+ // Selection
95
+ cy.get('select').select('value');
96
+ cy.get('select').select(['opt1', 'opt2']); // Multiple
97
+
98
+ // Checkboxes/Radio
99
+ cy.get('input[type="checkbox"]').check();
100
+ cy.get('input[type="checkbox"]').uncheck();
101
+ cy.get('input[type="radio"]').check('value');
102
+
103
+ // Focus/Blur
104
+ cy.get('input').focus();
105
+ cy.get('input').blur();
106
+
107
+ // Scrolling
108
+ cy.scrollTo('bottom');
109
+ cy.get('.list').scrollTo(0, 500);
110
+
111
+ // File upload
112
+ cy.get('input[type="file"]').selectFile('path/to/file.pdf');
113
+ ```
114
+
115
+ ## Assertions
116
+
117
+ ```typescript
118
+ // Visibility
119
+ cy.get('button').should('be.visible');
120
+ cy.get('modal').should('not.exist');
121
+ cy.get('loader').should('not.be.visible');
122
+
123
+ // Text
124
+ cy.get('h1').should('have.text', 'Exact Text');
125
+ cy.get('h1').should('contain', 'Partial');
126
+ cy.get('input').should('have.value', 'input value');
127
+
128
+ // Attributes
129
+ cy.get('a').should('have.attr', 'href', '/link');
130
+ cy.get('button').should('have.class', 'active');
131
+ cy.get('button').should('be.enabled');
132
+ cy.get('button').should('be.disabled');
133
+ cy.get('input').should('be.checked');
134
+
135
+ // Count
136
+ cy.get('li').should('have.length', 3);
137
+ cy.get('li').should('have.length.greaterThan', 2);
138
+
139
+ // URL
140
+ cy.url().should('include', '/dashboard');
141
+ cy.url().should('eq', 'http://localhost:3000/dashboard');
142
+
143
+ // Chaining
144
+ cy.get('input')
145
+ .should('be.visible')
146
+ .and('have.attr', 'placeholder', 'Email')
147
+ .and('be.enabled');
148
+ ```
149
+
150
+ ## Network Interception
151
+
152
+ ```typescript
153
+ // Stub a response
154
+ cy.intercept('GET', '/api/users', {
155
+ statusCode: 200,
156
+ body: [{ id: 1, name: 'John' }],
157
+ }).as('getUsers');
158
+
159
+ cy.visit('/users');
160
+ cy.wait('@getUsers');
161
+ cy.contains('John').should('be.visible');
162
+
163
+ // Wait for real request
164
+ cy.intercept('POST', '/api/login').as('login');
165
+ cy.get('form').submit();
166
+ cy.wait('@login').its('response.statusCode').should('eq', 200);
167
+
168
+ // Modify response
169
+ cy.intercept('GET', '/api/data', (req) => {
170
+ req.reply((res) => {
171
+ res.body.modified = true;
172
+ return res;
173
+ });
174
+ });
175
+
176
+ // Delay response
177
+ cy.intercept('GET', '/api/slow', (req) => {
178
+ req.on('response', (res) => {
179
+ res.setDelay(2000);
180
+ });
181
+ });
182
+
183
+ // Simulate error
184
+ cy.intercept('GET', '/api/data', {
185
+ statusCode: 500,
186
+ body: { error: 'Server error' },
187
+ });
188
+ ```
189
+
190
+ ## Fixtures
191
+
192
+ ```typescript
193
+ // cypress/fixtures/users.json
194
+ [
195
+ { "id": 1, "name": "John", "email": "john@example.com" },
196
+ { "id": 2, "name": "Jane", "email": "jane@example.com" }
197
+ ]
198
+
199
+ // In tests
200
+ cy.fixture('users').then((users) => {
201
+ cy.intercept('GET', '/api/users', users);
202
+ });
203
+
204
+ // Or directly
205
+ cy.intercept('GET', '/api/users', { fixture: 'users.json' });
206
+ ```
207
+
208
+ ## Custom Commands
209
+
210
+ ```typescript
211
+ // cypress/support/commands.ts
212
+ Cypress.Commands.add('login', (email: string, password: string) => {
213
+ cy.session([email, password], () => {
214
+ cy.visit('/login');
215
+ cy.get('[data-testid="email"]').type(email);
216
+ cy.get('[data-testid="password"]').type(password);
217
+ cy.get('[data-testid="submit"]').click();
218
+ cy.url().should('include', '/dashboard');
219
+ });
220
+ });
221
+
222
+ Cypress.Commands.add('getBySel', (selector: string) => {
223
+ return cy.get(`[data-testid="${selector}"]`);
224
+ });
225
+
226
+ // Usage
227
+ cy.login('user@example.com', 'password');
228
+ cy.getBySel('submit-button').click();
229
+
230
+ // TypeScript declarations (cypress/support/index.d.ts)
231
+ declare namespace Cypress {
232
+ interface Chainable {
233
+ login(email: string, password: string): Chainable<void>;
234
+ getBySel(selector: string): Chainable<JQuery<HTMLElement>>;
235
+ }
236
+ }
237
+ ```
238
+
239
+ ## Component Testing
240
+
241
+ ```typescript
242
+ // Button.cy.tsx
243
+ import Button from './Button';
244
+
245
+ describe('Button', () => {
246
+ it('renders with text', () => {
247
+ cy.mount(<Button>Click me</Button>);
248
+ cy.contains('Click me').should('be.visible');
249
+ });
250
+
251
+ it('calls onClick when clicked', () => {
252
+ const onClick = cy.stub().as('onClick');
253
+ cy.mount(<Button onClick={onClick}>Click</Button>);
254
+
255
+ cy.get('button').click();
256
+ cy.get('@onClick').should('have.been.calledOnce');
257
+ });
258
+
259
+ it('can be disabled', () => {
260
+ cy.mount(<Button disabled>Disabled</Button>);
261
+ cy.get('button').should('be.disabled');
262
+ });
263
+ });
264
+ ```
265
+
266
+ ## Configuration (cypress.config.ts)
267
+
268
+ ```typescript
269
+ import { defineConfig } from 'cypress';
270
+
271
+ export default defineConfig({
272
+ e2e: {
273
+ baseUrl: 'http://localhost:3000',
274
+ specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
275
+ supportFile: 'cypress/support/e2e.ts',
276
+ viewportWidth: 1280,
277
+ viewportHeight: 720,
278
+ video: false,
279
+ screenshotOnRunFailure: true,
280
+ retries: {
281
+ runMode: 2,
282
+ openMode: 0,
283
+ },
284
+ setupNodeEvents(on, config) {
285
+ // Plugins
286
+ },
287
+ },
288
+ component: {
289
+ devServer: {
290
+ framework: 'react',
291
+ bundler: 'vite',
292
+ },
293
+ specPattern: 'src/**/*.cy.{js,jsx,ts,tsx}',
294
+ },
295
+ env: {
296
+ apiUrl: 'http://localhost:3001',
297
+ },
298
+ });
299
+ ```
300
+
301
+ ## Best Practices
302
+
303
+ - **Use data-testid** or data-cy attributes for selectors
304
+ - **Don't use cy.wait(ms)** - Use intercepts and assertions
305
+ - **Keep tests independent** - Each test should work in isolation
306
+ - **Use cy.session()** for login/auth to speed up tests
307
+ - **Avoid conditional testing** - Tests should be deterministic
308
+ - **Use aliases** for readability (`cy.get(...).as('button')`)
309
+ - **Chain assertions** instead of multiple `cy.get()` calls
310
+ - **Test user flows** not implementation details
311
+
312
+ ## Anti-Patterns to Avoid
313
+
314
+ ```typescript
315
+ // ❌ Don't use arbitrary waits
316
+ cy.wait(5000);
317
+
318
+ // ✅ Wait for specific conditions
319
+ cy.get('button').should('be.visible');
320
+ cy.intercept('/api/*').as('api');
321
+ cy.wait('@api');
322
+
323
+ // ❌ Don't rely on DOM structure
324
+ cy.get('div > div > ul > li:first-child');
325
+
326
+ // ✅ Use data attributes
327
+ cy.get('[data-testid="first-item"]');
328
+
329
+ // ❌ Don't share state between tests
330
+ let userId;
331
+ it('creates user', () => { userId = createUser(); });
332
+ it('uses user', () => { /* uses userId */ });
333
+
334
+ // ✅ Each test is independent
335
+ it('user flow', () => {
336
+ const user = createUser();
337
+ // use user in same test
338
+ });
339
+ ```
340
+
341
+ ## Debugging
342
+
343
+ ```bash
344
+ # Open Cypress UI
345
+ npx cypress open
346
+
347
+ # Run headless
348
+ npx cypress run
349
+
350
+ # Run specific spec
351
+ npx cypress run --spec "cypress/e2e/login.cy.ts"
352
+
353
+ # With browser
354
+ npx cypress run --browser chrome
355
+ ```
356
+
357
+ ```typescript
358
+ // In tests - pause and inspect
359
+ cy.pause();
360
+
361
+ // Debug in console
362
+ cy.get('button').then(($btn) => {
363
+ debugger;
364
+ console.log($btn.text());
365
+ });
366
+ ```
367
+
368
+ ## References
369
+
370
+ - Cypress Documentation: https://docs.cypress.io/
371
+ - Best Practices: https://docs.cypress.io/guides/references/best-practices
372
+ - API Reference: https://docs.cypress.io/api/table-of-contents
@@ -0,0 +1,176 @@
1
+ ---
2
+ name: jest
3
+ description: Jest testing framework expertise for JavaScript/TypeScript unit and integration testing. Covers test structure, mocking, assertions, snapshots, coverage, and CI integration. Use when writing tests for React, Node.js, or any JS/TS project.
4
+ category: testing
5
+ compatible_with:
6
+ - react-patterns
7
+ - nodejs-express
8
+ - github-actions
9
+ ---
10
+
11
+ # Jest Testing Framework
12
+
13
+ ## Instructions
14
+
15
+ 1. **Assess the testing need**: Determine if it's unit tests, integration tests, or snapshot tests.
16
+ 2. **Follow Jest conventions**:
17
+ - Test files: `*.test.ts`, `*.spec.ts`, or in `__tests__/` directory
18
+ - Use `describe` blocks to group related tests
19
+ - Use `it` or `test` for individual test cases
20
+ - Use `beforeEach`/`afterEach` for setup/teardown
21
+ 3. **Provide complete examples**: Include imports, mocks, and assertions.
22
+ 4. **Guide on mocking**: Explain `jest.mock()`, `jest.fn()`, and `jest.spyOn()`.
23
+ 5. **Cover edge cases**: Test error conditions, boundary values, and async behavior.
24
+
25
+ ## Test Structure
26
+
27
+ ```typescript
28
+ import { describe, it, expect, beforeEach, jest } from '@jest/globals';
29
+ import { myFunction } from './myModule';
30
+
31
+ describe('myFunction', () => {
32
+ beforeEach(() => {
33
+ jest.clearAllMocks();
34
+ });
35
+
36
+ it('should return expected value for valid input', () => {
37
+ const result = myFunction('valid');
38
+ expect(result).toBe('expected');
39
+ });
40
+
41
+ it('should throw error for invalid input', () => {
42
+ expect(() => myFunction(null)).toThrow('Invalid input');
43
+ });
44
+
45
+ it('should handle async operations', async () => {
46
+ const result = await myFunction('async');
47
+ expect(result).resolves.toBe('done');
48
+ });
49
+ });
50
+ ```
51
+
52
+ ## Mocking Patterns
53
+
54
+ ### Mock a module
55
+ ```typescript
56
+ jest.mock('./database', () => ({
57
+ query: jest.fn().mockResolvedValue([{ id: 1 }]),
58
+ }));
59
+ ```
60
+
61
+ ### Mock a function
62
+ ```typescript
63
+ const mockCallback = jest.fn((x) => x + 1);
64
+ expect(mockCallback(1)).toBe(2);
65
+ expect(mockCallback).toHaveBeenCalledWith(1);
66
+ ```
67
+
68
+ ### Spy on methods
69
+ ```typescript
70
+ const spy = jest.spyOn(object, 'method');
71
+ object.method();
72
+ expect(spy).toHaveBeenCalled();
73
+ spy.mockRestore();
74
+ ```
75
+
76
+ ### Mock timers
77
+ ```typescript
78
+ jest.useFakeTimers();
79
+ setTimeout(callback, 1000);
80
+ jest.advanceTimersByTime(1000);
81
+ expect(callback).toHaveBeenCalled();
82
+ ```
83
+
84
+ ## Common Assertions
85
+
86
+ ```typescript
87
+ // Equality
88
+ expect(value).toBe(exact); // Strict equality
89
+ expect(value).toEqual(object); // Deep equality
90
+ expect(value).toStrictEqual(object); // Deep + type equality
91
+
92
+ // Truthiness
93
+ expect(value).toBeTruthy();
94
+ expect(value).toBeFalsy();
95
+ expect(value).toBeNull();
96
+ expect(value).toBeUndefined();
97
+ expect(value).toBeDefined();
98
+
99
+ // Numbers
100
+ expect(value).toBeGreaterThan(3);
101
+ expect(value).toBeLessThanOrEqual(5);
102
+ expect(value).toBeCloseTo(0.3, 5); // Floating point
103
+
104
+ // Strings
105
+ expect(string).toMatch(/pattern/);
106
+ expect(string).toContain('substring');
107
+
108
+ // Arrays
109
+ expect(array).toContain(item);
110
+ expect(array).toHaveLength(3);
111
+
112
+ // Objects
113
+ expect(object).toHaveProperty('key', 'value');
114
+ expect(object).toMatchObject({ partial: 'match' });
115
+
116
+ // Errors
117
+ expect(() => fn()).toThrow();
118
+ expect(() => fn()).toThrow('message');
119
+ expect(() => fn()).toThrow(ErrorClass);
120
+
121
+ // Async
122
+ await expect(promise).resolves.toBe(value);
123
+ await expect(promise).rejects.toThrow();
124
+ ```
125
+
126
+ ## Configuration (jest.config.js)
127
+
128
+ ```javascript
129
+ module.exports = {
130
+ preset: 'ts-jest',
131
+ testEnvironment: 'node', // or 'jsdom' for browser
132
+ roots: ['<rootDir>/src'],
133
+ testMatch: ['**/*.test.ts', '**/*.spec.ts'],
134
+ collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'],
135
+ coverageThreshold: {
136
+ global: { branches: 80, functions: 80, lines: 80 },
137
+ },
138
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
139
+ moduleNameMapper: {
140
+ '^@/(.*)$': '<rootDir>/src/$1',
141
+ },
142
+ };
143
+ ```
144
+
145
+ ## Best Practices
146
+
147
+ - **One assertion per test** when possible for clear failure messages
148
+ - **Arrange-Act-Assert** pattern for test structure
149
+ - **Mock external dependencies** (APIs, databases, file system)
150
+ - **Don't test implementation details** - test behavior and outcomes
151
+ - **Use meaningful test names** that describe the expected behavior
152
+ - **Keep tests fast** - mock slow operations
153
+ - **Run tests in CI** with coverage reporting
154
+ - **Snapshot tests** for UI components, but review changes carefully
155
+
156
+ ## React Testing (with @testing-library/react)
157
+
158
+ ```typescript
159
+ import { render, screen, fireEvent } from '@testing-library/react';
160
+ import { Button } from './Button';
161
+
162
+ test('calls onClick when clicked', () => {
163
+ const handleClick = jest.fn();
164
+ render(<Button onClick={handleClick}>Click me</Button>);
165
+
166
+ fireEvent.click(screen.getByRole('button'));
167
+
168
+ expect(handleClick).toHaveBeenCalledTimes(1);
169
+ });
170
+ ```
171
+
172
+ ## References
173
+
174
+ - Jest Documentation: https://jestjs.io/docs/getting-started
175
+ - Testing Library: https://testing-library.com/docs/
176
+ - Jest Cheat Sheet: https://github.com/sapegin/jest-cheat-sheet