codeforge-dev 1.4.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.
Files changed (131) hide show
  1. package/.devcontainer/.env +22 -0
  2. package/.devcontainer/CHANGELOG.md +197 -0
  3. package/.devcontainer/CLAUDE.md +117 -0
  4. package/.devcontainer/README.md +222 -0
  5. package/.devcontainer/config/main-system-prompt.md +502 -0
  6. package/.devcontainer/config/settings.json +47 -0
  7. package/.devcontainer/devcontainer.json +94 -0
  8. package/.devcontainer/features/README.md +113 -0
  9. package/.devcontainer/features/agent-browser/README.md +65 -0
  10. package/.devcontainer/features/agent-browser/devcontainer-feature.json +23 -0
  11. package/.devcontainer/features/agent-browser/install.sh +79 -0
  12. package/.devcontainer/features/ast-grep/README.md +24 -0
  13. package/.devcontainer/features/ast-grep/devcontainer-feature.json +24 -0
  14. package/.devcontainer/features/ast-grep/install.sh +51 -0
  15. package/.devcontainer/features/ccstatusline/README.md +296 -0
  16. package/.devcontainer/features/ccstatusline/devcontainer-feature.json +19 -0
  17. package/.devcontainer/features/ccstatusline/install.sh +290 -0
  18. package/.devcontainer/features/ccusage/README.md +205 -0
  19. package/.devcontainer/features/ccusage/devcontainer-feature.json +38 -0
  20. package/.devcontainer/features/ccusage/install.sh +132 -0
  21. package/.devcontainer/features/claude-code/README.md +498 -0
  22. package/.devcontainer/features/claude-code/config/settings.json +36 -0
  23. package/.devcontainer/features/claude-code/config/system-prompt.md +118 -0
  24. package/.devcontainer/features/claude-code/config/world-building-sp.md +1432 -0
  25. package/.devcontainer/features/claude-code/devcontainer-feature.json +42 -0
  26. package/.devcontainer/features/claude-code/install.sh +466 -0
  27. package/.devcontainer/features/claude-monitor/README.md +74 -0
  28. package/.devcontainer/features/claude-monitor/devcontainer-feature.json +38 -0
  29. package/.devcontainer/features/claude-monitor/install.sh +99 -0
  30. package/.devcontainer/features/lsp-servers/README.md +85 -0
  31. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +40 -0
  32. package/.devcontainer/features/lsp-servers/install.sh +116 -0
  33. package/.devcontainer/features/mcp-qdrant/CHANGES.md +399 -0
  34. package/.devcontainer/features/mcp-qdrant/README.md +474 -0
  35. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +57 -0
  36. package/.devcontainer/features/mcp-qdrant/install.sh +295 -0
  37. package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +129 -0
  38. package/.devcontainer/features/mcp-reasoner/README.md +177 -0
  39. package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +20 -0
  40. package/.devcontainer/features/mcp-reasoner/install.sh +177 -0
  41. package/.devcontainer/features/mcp-reasoner/poststart-hook.sh +67 -0
  42. package/.devcontainer/features/notify-hook/README.md +86 -0
  43. package/.devcontainer/features/notify-hook/devcontainer-feature.json +23 -0
  44. package/.devcontainer/features/notify-hook/install.sh +38 -0
  45. package/.devcontainer/features/splitrail/README.md +140 -0
  46. package/.devcontainer/features/splitrail/devcontainer-feature.json +34 -0
  47. package/.devcontainer/features/splitrail/install.sh +129 -0
  48. package/.devcontainer/features/tree-sitter/README.md +138 -0
  49. package/.devcontainer/features/tree-sitter/devcontainer-feature.json +52 -0
  50. package/.devcontainer/features/tree-sitter/install.sh +173 -0
  51. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +106 -0
  52. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +7 -0
  53. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json +17 -0
  54. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-file.py +101 -0
  55. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +7 -0
  56. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +17 -0
  57. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +137 -0
  58. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/.claude-plugin/plugin.json +8 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/SKILL.md +387 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/cli-flags-and-output.md +312 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/claude-code-headless/references/sdk-and-mcp.md +569 -0
  62. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/SKILL.md +309 -0
  63. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/compose-services.md +438 -0
  64. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker/references/dockerfile-patterns.md +340 -0
  65. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/SKILL.md +412 -0
  66. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/container-lifecycle.md +388 -0
  67. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/docker-py/references/resources-and-security.md +444 -0
  68. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/SKILL.md +344 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/middleware-and-lifespan.md +254 -0
  70. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/pydantic-models.md +245 -0
  71. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/routing-and-dependencies.md +255 -0
  72. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/fastapi/references/sse-and-streaming.md +318 -0
  73. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/SKILL.md +345 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/agents-and-tools.md +271 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/pydantic-ai/references/models-and-streaming.md +422 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/SKILL.md +220 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/cross-vendor-principles.md +139 -0
  78. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/patterns-and-antipatterns.md +376 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/skill-building/references/skill-authoring-patterns.md +356 -0
  80. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/SKILL.md +329 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/advanced-queries.md +314 -0
  82. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/javascript-patterns.md +323 -0
  83. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/python-patterns.md +354 -0
  84. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/sqlite/references/schema-and-pragmas.md +326 -0
  85. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/SKILL.md +356 -0
  86. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/ai-sdk-svelte.md +128 -0
  87. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/component-patterns.md +332 -0
  88. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/layercake.md +203 -0
  89. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/migration-guide.md +350 -0
  90. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/runes-and-reactivity.md +328 -0
  91. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/spa-and-routing.md +262 -0
  92. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/svelte5/references/svelte-dnd-action.md +181 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/SKILL.md +414 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/fastapi-testing.md +411 -0
  95. package/.devcontainer/plugins/devs-marketplace/plugins/codedirective-skills/skills/testing/references/svelte-testing.md +538 -0
  96. package/.devcontainer/plugins/devs-marketplace/plugins/codeforge-lsp/.claude-plugin/plugin.json +7 -0
  97. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json +7 -0
  98. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json +17 -0
  99. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +110 -0
  100. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json +7 -0
  101. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +17 -0
  102. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +7 -0
  103. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +17 -0
  104. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json +7 -0
  105. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +17 -0
  106. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +108 -0
  107. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272create-pr.md +337 -0
  108. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272new.md +166 -0
  109. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272review-commit.md +290 -0
  110. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket/357/200/272work.md +257 -0
  111. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json +8 -0
  112. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md +184 -0
  113. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +6 -0
  114. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +14 -0
  115. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +989 -0
  116. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +33 -0
  117. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
  118. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +71 -0
  119. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +68 -0
  120. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +120 -0
  121. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +133 -0
  122. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +253 -0
  123. package/.devcontainer/scripts/setup-aliases.sh +80 -0
  124. package/.devcontainer/scripts/setup-config.sh +28 -0
  125. package/.devcontainer/scripts/setup-irie-claude.sh +32 -0
  126. package/.devcontainer/scripts/setup-plugins.sh +80 -0
  127. package/.devcontainer/scripts/setup.sh +58 -0
  128. package/LICENSE.txt +674 -0
  129. package/README.md +267 -0
  130. package/package.json +44 -0
  131. package/setup.js +83 -0
@@ -0,0 +1,414 @@
1
+ ---
2
+ name: testing
3
+ description: >-
4
+ This skill should be used when the user asks to "write tests for a FastAPI endpoint",
5
+ "test a Svelte component", "set up pytest fixtures for FastAPI",
6
+ "configure Vitest for SvelteKit", "mock dependencies in FastAPI tests",
7
+ "test SSE streaming endpoints", "write component tests with Testing Library",
8
+ "set up database fixtures for API tests", or discusses pytest, httpx AsyncClient,
9
+ Vitest, @testing-library/svelte, dependency overrides, or test fixtures.
10
+ version: 0.1.0
11
+ ---
12
+
13
+ # Testing (FastAPI + Svelte)
14
+
15
+ ## Mental Model
16
+
17
+ Testing verifies **behavior at public interfaces**, not implementation details. For a FastAPI backend, the public interface is the HTTP contract -- request in, response out. For a Svelte frontend, the public interface is what the user sees and interacts with -- rendered text, form inputs, button clicks.
18
+
19
+ Both stacks share core principles: isolate the system under test by replacing external dependencies (databases, APIs, auth) with controlled substitutes; assert on observable outcomes rather than internal state; structure tests as **arrange-act-assert** with clear boundaries between each phase.
20
+
21
+ The key difference is the isolation mechanism. FastAPI uses `app.dependency_overrides` to swap injected dependencies at the application level -- the real handler code runs with fake resources. Svelte tests use `vi.mock()` or MSW (Mock Service Worker) to intercept network calls at the fetch layer -- the real component code runs with fake responses.
22
+
23
+ Assume pytest with pytest-anyio for FastAPI, and Vitest with @testing-library/svelte for Svelte 5.
24
+
25
+ ---
26
+
27
+ ## FastAPI: AsyncClient Setup
28
+
29
+ The modern async testing pattern uses `httpx.AsyncClient` with `ASGITransport`:
30
+
31
+ ```python
32
+ import pytest
33
+ from httpx import ASGITransport, AsyncClient
34
+ from app.main import app
35
+
36
+ @pytest.fixture
37
+ async def client():
38
+ transport = ASGITransport(app=app)
39
+ async with AsyncClient(transport=transport, base_url="http://test") as ac:
40
+ yield ac
41
+
42
+ @pytest.mark.anyio
43
+ async def test_read_items(client: AsyncClient):
44
+ response = await client.get("/items/")
45
+ assert response.status_code == 200
46
+ assert isinstance(response.json(), list)
47
+ ```
48
+
49
+ `AsyncClient` does **not** trigger lifespan events. For applications that depend on startup/shutdown:
50
+
51
+ ```python
52
+ from asgi_lifespan import LifespanManager
53
+
54
+ @pytest.fixture
55
+ async def client():
56
+ async with LifespanManager(app) as manager:
57
+ transport = ASGITransport(app=manager.app)
58
+ async with AsyncClient(transport=transport, base_url="http://test") as ac:
59
+ yield ac
60
+ ```
61
+
62
+ The synchronous `TestClient` (from Starlette) is simpler but cannot be used inside `async def` tests:
63
+
64
+ ```python
65
+ from fastapi.testclient import TestClient
66
+
67
+ def test_read_items():
68
+ with TestClient(app) as client:
69
+ response = client.get("/items/")
70
+ assert response.status_code == 200
71
+ ```
72
+
73
+ > **Deep dive:** See `references/fastapi-testing.md` for dependency override patterns, SSE stream testing with httpx-sse, WebSocket testing, background task testing, and database fixture patterns with aiosqlite.
74
+
75
+ ---
76
+
77
+ ## FastAPI: Dependency Overrides
78
+
79
+ Replace real dependencies with test doubles using `app.dependency_overrides`:
80
+
81
+ ```python
82
+ from app.dependencies import get_db, get_current_user
83
+
84
+ async def mock_db():
85
+ yield FakeDatabase()
86
+
87
+ async def mock_user():
88
+ return User(id=1, email="test@example.com", role="admin")
89
+
90
+ @pytest.fixture
91
+ async def client():
92
+ app.dependency_overrides[get_db] = mock_db
93
+ app.dependency_overrides[get_current_user] = mock_user
94
+ transport = ASGITransport(app=app)
95
+ async with AsyncClient(transport=transport, base_url="http://test") as ac:
96
+ yield ac
97
+ app.dependency_overrides.clear()
98
+ ```
99
+
100
+ The override dict maps original dependency callables to replacement callables. Generator dependencies (using `yield`) work as overrides with proper setup/teardown semantics.
101
+
102
+ ### Factory Fixture for Dynamic Overrides
103
+
104
+ When different tests need different users or roles, use a factory fixture:
105
+
106
+ ```python
107
+ @pytest.fixture
108
+ def override_user():
109
+ """Return a function that sets the current user override."""
110
+ def _override(user: User):
111
+ app.dependency_overrides[get_current_user] = lambda: user
112
+ return _override
113
+
114
+ @pytest.mark.anyio
115
+ async def test_admin_access(client, override_user):
116
+ override_user(User(id=1, email="admin@test.com", role="admin"))
117
+ response = await client.get("/admin/settings")
118
+ assert response.status_code == 200
119
+
120
+ @pytest.mark.anyio
121
+ async def test_user_forbidden(client, override_user):
122
+ override_user(User(id=2, email="user@test.com", role="user"))
123
+ response = await client.get("/admin/settings")
124
+ assert response.status_code == 403
125
+ ```
126
+
127
+ ---
128
+
129
+ ## FastAPI: SSE Stream Testing
130
+
131
+ Test SSE endpoints using `httpx-sse` to parse server-sent events:
132
+
133
+ ```python
134
+ from httpx_sse import aconnect_sse
135
+
136
+ @pytest.mark.anyio
137
+ async def test_sse_stream():
138
+ async with AsyncClient(
139
+ transport=ASGITransport(app=app),
140
+ base_url="http://test"
141
+ ) as ac:
142
+ async with aconnect_sse(ac, "GET", "/events") as event_source:
143
+ events = []
144
+ async for sse in event_source.aiter_sse():
145
+ events.append(sse)
146
+ if len(events) >= 3:
147
+ break
148
+
149
+ assert len(events) == 3
150
+ assert events[0].event == "update"
151
+ ```
152
+
153
+ For raw stream testing without `httpx-sse`, use `ac.stream()` and parse `data:` lines manually. The `httpx-sse` approach is preferred because it handles multi-line data fields, event types, and reconnection IDs correctly.
154
+
155
+ ---
156
+
157
+ ## Svelte: Vitest Configuration
158
+
159
+ ### Installation
160
+
161
+ ```bash
162
+ npm install -D vitest jsdom @testing-library/svelte @testing-library/jest-dom @testing-library/user-event
163
+ ```
164
+
165
+ ### Configuration
166
+
167
+ ```javascript
168
+ // vite.config.js
169
+ import { defineConfig } from 'vitest/config'
170
+ import { sveltekit } from '@sveltejs/kit/vite'
171
+ import { svelteTesting } from '@testing-library/svelte/vite'
172
+
173
+ export default defineConfig({
174
+ plugins: [sveltekit(), svelteTesting()],
175
+ test: {
176
+ environment: 'jsdom',
177
+ setupFiles: ['./vitest-setup.js'],
178
+ },
179
+ })
180
+ ```
181
+
182
+ ```javascript
183
+ // vitest-setup.js
184
+ import '@testing-library/jest-dom/vitest'
185
+ ```
186
+
187
+ The `svelteTesting()` plugin handles automatic cleanup after each test. To use Svelte 5 runes (`$state`, `$derived`) directly in test files, name the file with `.svelte.test.ts` extension.
188
+
189
+ > **Deep dive:** See `references/svelte-testing.md` for component rendering patterns, user event simulation, async state updates, mocking fetch and SSE, mocking SvelteKit modules, and snapshot testing.
190
+
191
+ ---
192
+
193
+ ## Svelte: Component Testing
194
+
195
+ Render components with `render()` and query the DOM with accessible queries:
196
+
197
+ ```javascript
198
+ import { render, screen } from '@testing-library/svelte'
199
+ import userEvent from '@testing-library/user-event'
200
+ import { expect, test } from 'vitest'
201
+ import Counter from './Counter.svelte'
202
+
203
+ test('increments count on click', async () => {
204
+ const user = userEvent.setup()
205
+ render(Counter, { initial: 0 })
206
+
207
+ const button = screen.getByRole('button')
208
+ expect(button).toHaveTextContent('0')
209
+
210
+ await user.click(button)
211
+ expect(button).toHaveTextContent('1')
212
+ })
213
+ ```
214
+
215
+ ### Query Priority
216
+
217
+ Prefer queries that reflect how users find elements:
218
+
219
+ 1. `getByRole` -- accessible role (`button`, `heading`, `textbox`)
220
+ 2. `getByLabelText` -- form elements by label
221
+ 3. `getByText` -- visible text content
222
+ 4. `getByPlaceholderText` -- input placeholders
223
+ 5. `getByTestId` -- last resort, `data-testid` attribute
224
+
225
+ ### Query Types
226
+
227
+ | Prefix | 0 matches | 1 match | >1 matches | Async |
228
+ |--------|-----------|---------|------------|-------|
229
+ | `getBy` | throws | returns | throws | No |
230
+ | `queryBy` | `null` | returns | throws | No |
231
+ | `findBy` | throws | returns | throws | Yes |
232
+
233
+ Use `getBy` for elements that must exist, `queryBy` to assert absence, `findBy` to wait for async rendering.
234
+
235
+ ---
236
+
237
+ ## Svelte: Async State and Waiting
238
+
239
+ Svelte 5 components update asynchronously. Testing Library provides several mechanisms to handle this:
240
+
241
+ ### findBy Queries
242
+
243
+ `findBy` queries poll until the element appears (default timeout: 1000ms). Use them for components that load data asynchronously:
244
+
245
+ ```javascript
246
+ test('loads and displays items', async () => {
247
+ render(ItemList)
248
+
249
+ // Waits for the element to appear in the DOM
250
+ const item = await screen.findByText('Loaded Item', {}, { timeout: 3000 })
251
+ expect(item).toBeInTheDocument()
252
+ })
253
+ ```
254
+
255
+ ### waitFor
256
+
257
+ `waitFor` repeatedly runs an assertion until it passes or times out. Use it when no single element query captures the expected state:
258
+
259
+ ```javascript
260
+ import { waitFor } from '@testing-library/svelte'
261
+
262
+ test('counter reaches target', async () => {
263
+ render(AnimatedCounter, { target: 10 })
264
+
265
+ await waitFor(() => {
266
+ expect(screen.getByTestId('count')).toHaveTextContent('10')
267
+ }, { timeout: 2000 })
268
+ })
269
+ ```
270
+
271
+ ### Mocking Fetch with MSW
272
+
273
+ MSW (Mock Service Worker) intercepts requests at the network level. Configure it in `vitest-setup.js` for global availability:
274
+
275
+ ```javascript
276
+ import { http, HttpResponse } from 'msw'
277
+ import { setupServer } from 'msw/node'
278
+
279
+ const server = setupServer(
280
+ http.get('/api/items', () => HttpResponse.json({ items: ['Apple'] })),
281
+ )
282
+
283
+ beforeAll(() => server.listen())
284
+ afterEach(() => server.resetHandlers())
285
+ afterAll(() => server.close())
286
+ ```
287
+
288
+ Override handlers per test for error scenarios:
289
+
290
+ ```javascript
291
+ test('handles server error', async () => {
292
+ server.use(http.get('/api/items', () => new HttpResponse(null, { status: 500 })))
293
+ render(ItemList)
294
+ expect(await screen.findByText('Error loading items')).toBeInTheDocument()
295
+ })
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Shared Patterns
301
+
302
+ ### Test Organization
303
+
304
+ Mirror the source directory structure:
305
+
306
+ ```
307
+ src/
308
+ routes/
309
+ items/
310
+ +page.svelte
311
+ lib/
312
+ api.ts
313
+ tests/
314
+ routes/
315
+ items/
316
+ page.test.ts
317
+ lib/
318
+ api.test.ts
319
+ ```
320
+
321
+ For FastAPI:
322
+
323
+ ```
324
+ app/
325
+ routers/
326
+ items.py
327
+ services/
328
+ item_service.py
329
+ tests/
330
+ routers/
331
+ test_items.py
332
+ services/
333
+ test_item_service.py
334
+ ```
335
+
336
+ ### Arrange-Act-Assert
337
+
338
+ ```python
339
+ @pytest.mark.anyio
340
+ async def test_create_item(client: AsyncClient):
341
+ # Arrange
342
+ payload = {"name": "Widget", "price": 9.99}
343
+
344
+ # Act
345
+ response = await client.post("/items/", json=payload)
346
+
347
+ # Assert
348
+ assert response.status_code == 201
349
+ data = response.json()
350
+ assert data["name"] == "Widget"
351
+ assert data["price"] == 9.99
352
+ ```
353
+
354
+ ```javascript
355
+ test('displays item name', async () => {
356
+ // Arrange
357
+ const item = { name: 'Widget', price: 9.99 }
358
+
359
+ // Act
360
+ render(ItemCard, { item })
361
+
362
+ // Assert
363
+ expect(screen.getByText('Widget')).toBeInTheDocument()
364
+ expect(screen.getByText('$9.99')).toBeInTheDocument()
365
+ })
366
+ ```
367
+
368
+ ### Fixture Composition
369
+
370
+ Compose small, focused fixtures rather than building monolithic setup functions:
371
+
372
+ ```python
373
+ @pytest.fixture
374
+ async def db_session(engine):
375
+ async with async_sessionmaker(engine)() as session:
376
+ async with session.begin():
377
+ yield session
378
+ await session.rollback()
379
+
380
+ @pytest.fixture
381
+ async def seeded_db(db_session):
382
+ db_session.add(Item(name="Existing", price=5.00))
383
+ await db_session.flush()
384
+ return db_session
385
+
386
+ @pytest.fixture
387
+ async def client(db_session):
388
+ app.dependency_overrides[get_db] = lambda: db_session
389
+ async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
390
+ yield ac
391
+ app.dependency_overrides.clear()
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Ambiguity Policy
397
+
398
+ These defaults apply when the user does not specify a preference. State the assumption when making a choice so the user can override:
399
+
400
+ - **Test runner:** Default to pytest with pytest-anyio for FastAPI. Default to Vitest for Svelte.
401
+ - **Async test style:** Default to `@pytest.mark.anyio` with `AsyncClient`. Use sync `TestClient` only when async is not needed.
402
+ - **HTTP mocking (Svelte):** Default to MSW for fetch mocking. Use `vi.fn()` on `globalThis.fetch` for simple cases without many endpoints.
403
+ - **Component queries:** Default to `screen.getByRole()`. Use `getByTestId` only when no accessible role or text is available.
404
+ - **Database isolation:** Default to per-test transaction rollback. Use per-test database creation only when testing migrations or schema changes.
405
+ - **User events:** Default to `userEvent.setup()` over `fireEvent`. `userEvent` simulates realistic browser interaction sequences.
406
+
407
+ ---
408
+
409
+ ## Reference Files
410
+
411
+ | File | Contents |
412
+ |------|----------|
413
+ | `references/fastapi-testing.md` | AsyncClient setup details, dependency override patterns, SSE stream testing with httpx-sse, WebSocket testing, background task testing, database fixtures with aiosqlite, pytest configuration |
414
+ | `references/svelte-testing.md` | Vitest configuration for SvelteKit, @testing-library/svelte render API, user event simulation, async state updates (act, flushSync, waitFor, findBy), mocking fetch and SSE with MSW, mocking SvelteKit modules ($app/*), snapshot testing |