@@ -191,6 +191,56 @@ describe('ObservableValue', () => {
191191})
192192```
193193
194+ ## Backend Service Testing with Test Helpers
195+
196+ ### Using `withTestInjector`
197+
198+ For testing backend services and REST actions, use the shared test helpers:
199+
200+ ```typescript
201+ import { describe, it, expect, vi } from 'vitest'
202+ import { getRepository } from '@furystack/repository'
203+ import { ServiceDefinition, ServiceStatus } from 'common'
204+
205+ import { withTestInjector, createMockActionContext } from '../test-helpers.js'
206+
207+ describe('MyAction', () => {
208+ it('should return data for valid request', () =>
209+ withTestInjector(async ({ injector, elevated }) => {
210+ // Seed test data
211+ await getRepository(elevated).getDataSetFor(ServiceDefinition, 'id').add(elevated, {
212+ id: 'svc-1',
213+ stackName: 'test-stack',
214+ displayName: 'Test Service',
215+ // ... other required fields
216+ })
217+
218+ // Mock external services via setExplicitInstance
219+ const mockService = { someMethod: vi.fn().mockResolvedValue('result') }
220+ injector.setExplicitInstance(mockService as unknown as MyService, MyService)
221+
222+ // Create and execute action
223+ const ctx = createMockActionContext({
224+ injector: elevated,
225+ urlParams: { id: 'svc-1' },
226+ })
227+ const result = await myAction(ctx)
228+
229+ // Assert
230+ expect(result.chunk.status).toBe(200)
231+ }),
232+ )
233+ })
234+ ```
235+
236+ ### Key patterns
237+
238+ - `withTestInjector` provides an `injector` + `elevated` (system-level) context with all InMemoryStores pre-configured
239+ - `createMockActionContext` creates a typed request context for REST action testing
240+ - Use `getRepository(elevated).getDataSetFor(...)` to seed and verify data
241+ - Use `injector.setExplicitInstance(mock, ServiceClass)` for mocking DI services
242+ - Both injectors are automatically disposed after each test
243+
194244## E2E Testing with Playwright
195245
196246### Test File Location
@@ -278,9 +328,8 @@ test.describe('Authentication', () => {
278328 await page.locator('button', { hasText: 'Login' }).click()
279329
280330 // Verify logged in state
281- const welcomeTitle = page.locator('hello-world div h2')
282- await expect(welcomeTitle).toBeVisible()
283- await expect(welcomeTitle).toHaveText('Hello, testuser !')
331+ const dashboard = page.locator('page-dashboard')
332+ await expect(dashboard).toBeVisible()
284333
285334 // Logout
286335 const logoutButton = page.locator('shade-app-bar button >> text="Log Out"')
0 commit comments