even more typescript fixes
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 25m5s
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 25m5s
This commit is contained in:
@@ -505,3 +505,115 @@ expect(element.className).toMatch(/dark:bg-teal-\d+/);
|
||||
```
|
||||
|
||||
See [ADR-057](../adr/0057-test-remediation-post-api-versioning.md) for lessons learned from the test remediation effort.
|
||||
|
||||
## TypeScript Type Safety in Tests (ADR-060)
|
||||
|
||||
Tests must be fully type-safe. Common patterns for handling API response types and mock casting are documented below.
|
||||
|
||||
### Response Type Narrowing
|
||||
|
||||
API responses use discriminated unions (`ApiSuccessResponse<T> | ApiErrorResponse`). Access `.data` only after type narrowing.
|
||||
|
||||
**Utility Functions** (`src/tests/utils/testHelpers.ts`):
|
||||
|
||||
```typescript
|
||||
import { asSuccessResponse, asErrorResponse } from '@/tests/utils/testHelpers';
|
||||
|
||||
// Success response access
|
||||
const response = await request.get('/api/v1/users/1');
|
||||
const body = asSuccessResponse<User>(response.body);
|
||||
expect(body.data.id).toBe(1);
|
||||
|
||||
// Error response access
|
||||
const errorResponse = await request.post('/api/v1/users').send({});
|
||||
expect(errorResponse.status).toBe(400);
|
||||
const errorBody = asErrorResponse(errorResponse.body);
|
||||
expect(errorBody.error.code).toBe('VALIDATION_ERROR');
|
||||
```
|
||||
|
||||
### Mock Object Type Casting
|
||||
|
||||
Use appropriate casting based on type compatibility:
|
||||
|
||||
```typescript
|
||||
// Level 1: Type assertion for compatible shapes
|
||||
const mock = createMockUser() as User;
|
||||
|
||||
// Level 2: Unknown bridge for incompatible shapes
|
||||
const mock = partialMock as unknown as User;
|
||||
|
||||
// Level 3: Partial with required overrides
|
||||
const mock: User = { ...createPartialUser(), id: 1, email: 'test@test.com' };
|
||||
```
|
||||
|
||||
### Mock Function Casting
|
||||
|
||||
```typescript
|
||||
import { asMock } from '@/tests/utils/testHelpers';
|
||||
|
||||
// Cast vi.fn() to specific function type
|
||||
const mockFn = vi.fn();
|
||||
someService.register(asMock<UserService['create']>(mockFn));
|
||||
|
||||
// vi.fn() with explicit type parameters
|
||||
const mockFn = vi.fn<[string], Promise<User>>().mockResolvedValue(mockUser);
|
||||
|
||||
// vi.mocked() for mocked modules
|
||||
vi.mock('@/services/userService');
|
||||
const mockedService = vi.mocked(userService);
|
||||
mockedService.create.mockResolvedValue(mockUser);
|
||||
```
|
||||
|
||||
### Mock Logger for Controller Tests
|
||||
|
||||
Controllers require a Pino logger on `req.log`. Use the shared mock logger utility:
|
||||
|
||||
```typescript
|
||||
import { createMockLogger } from '@/tests/utils/testHelpers';
|
||||
|
||||
function createMockRequest(overrides = {}): ExpressRequest {
|
||||
return {
|
||||
body: {},
|
||||
cookies: {},
|
||||
log: createMockLogger(),
|
||||
res: { cookie: vi.fn() } as unknown as ExpressResponse,
|
||||
...overrides,
|
||||
} as unknown as ExpressRequest;
|
||||
}
|
||||
```
|
||||
|
||||
The `createMockLogger()` function returns a complete Pino logger mock with all methods (`info`, `debug`, `error`, `warn`, `fatal`, `trace`, `silent`, `child`) as `vi.fn()` mocks.
|
||||
|
||||
### MSW Handler Typing
|
||||
|
||||
Ensure MSW handlers return properly typed API responses:
|
||||
|
||||
```typescript
|
||||
import { ApiSuccessResponse } from '@/types/api';
|
||||
import { Flyer } from '@/types/flyer';
|
||||
|
||||
http.get('/api/v1/flyers', () => {
|
||||
const response: ApiSuccessResponse<Flyer[]> = {
|
||||
success: true,
|
||||
data: [mockFlyer],
|
||||
};
|
||||
return HttpResponse.json(response);
|
||||
});
|
||||
```
|
||||
|
||||
### Generic Type Parameters
|
||||
|
||||
Provide explicit generics when TypeScript cannot infer:
|
||||
|
||||
```typescript
|
||||
// Factory function generic
|
||||
const mock = createMockPaginatedResponse<Flyer>({ data: [mockFlyer] });
|
||||
|
||||
// Assertion generic
|
||||
expect(result).toEqual<ApiSuccessResponse<User>>({
|
||||
success: true,
|
||||
data: mockUser,
|
||||
});
|
||||
```
|
||||
|
||||
See [ADR-060](../adr/0060-typescript-test-error-remediation.md) for comprehensive patterns and remediation strategies.
|
||||
|
||||
Reference in New Issue
Block a user