Remove every as any from the test suite and enforce it
ESLint turned `no-explicit-any` off for test files, and 219 casts had accumulated across 12 of them. Two shapes, very different risks: most were `(useWorkspace as any).mockReturnValue(...)`, mere laziness that `vi.mocked()` does properly; the rest cast partial fixtures onto API types, which erases the contract exactly where a test should enforce it. That second kind matters here specifically. This project's recurring bug is a field silently dropped between backend and frontend -- the hand-written mapping in models/plan.py has done it twice, and apiFetch mishandling structured errors was the same shape. A test that casts an incomplete fixture to `any` keeps passing through precisely that change. A typed fixture layer already existed at tests/fixtures/mockData.ts and was imported by exactly zero tests: the right tool was there and invisible, so everyone reached for a cast. It is now a set of `make*(overrides: Partial<T>) => T` factories returning complete, valid objects, split by domain with a barrel. When a type gains a field, one factory fails to compile instead of thirty tests silently passing. With the count at zero the ESLint exemption is deleted, so this cannot regrow. Removing it immediately caught two `}: any` prop stubs that a grep for "as any" had missed; those mocks now use the real components' exported prop types. `as unknown as T` survives in five places, each a test deliberately feeding invalid input (an undefined breakdown, missing tierData, a bogus leave type). CLAUDE.md now documents that as the only accepted escape, and points at the factories. interface PollResponse in useScraperFetch.ts is exported so its shape can be derived rather than duplicated -- pollScraperSession declares Promise<unknown>, so there was no other honest way to type it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RXMhc9nT6sNz1SL4j8dznL
This commit is contained in:
Vendored
+254
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Shared test fixtures — return values of `src/context/*` and `src/hooks/*`.
|
||||
*
|
||||
* None of these hooks export their result interface (e.g. `useAnalytics`'s
|
||||
* `UseAnalyticsResult` is private to useAnalytics.ts), and this file
|
||||
* deliberately does not add exports to make that possible — `ReturnType<typeof
|
||||
* theHook>` gets the same fully-typed result without touching source files.
|
||||
* `import type` erases at compile time, so this doesn't pull the hooks'
|
||||
* runtime code (or React) into the test bundle.
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest'
|
||||
import type { useWorkspace } from '@/context/WorkspaceContext'
|
||||
import type { useAuth } from '@/context/AuthContext'
|
||||
import type { useAnalytics } from '@/hooks/useAnalytics'
|
||||
import type { useParentalLeave } from '@/hooks/useParentalLeave'
|
||||
import type { usePlannedDays } from '@/hooks/usePlannedDays'
|
||||
import type { useScraperFetch } from '@/hooks/useScraperFetch'
|
||||
import type { useWorkspaceMembers } from '@/hooks/useWorkspaceMembers'
|
||||
import type { useOptimizationTips } from '@/hooks/useOptimizationTips'
|
||||
import type { useDayOverrides } from '@/hooks/useDayOverrides'
|
||||
import type { useLeaveWishes } from '@/hooks/useLeaveWishes'
|
||||
import type { usePlanProposal } from '@/hooks/usePlanProposal'
|
||||
import type { usePlanApply } from '@/hooks/usePlanApply'
|
||||
import type { useVacationDays } from '@/hooks/useVacationDays'
|
||||
import type { useChildSettings } from '@/hooks/useChildSettings'
|
||||
import type { useCalendarDays } from '@/hooks/useCalendarDays'
|
||||
import type { useShowWeekNumbers } from '@/hooks/useCalendarPreferences'
|
||||
import { MOCK_CHILD, MOCK_PARENT_A, MOCK_VACATION_DAYS, MOCK_WORKSPACE, makeParentalLeaveFetch } from './mockData'
|
||||
import { makeWorkspaceOverview } from './analytics'
|
||||
import { makeCalendarWindow } from './calendar'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Contexts
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function makeWorkspaceContextValue(
|
||||
overrides: Partial<ReturnType<typeof useWorkspace>> = {},
|
||||
): ReturnType<typeof useWorkspace> {
|
||||
return {
|
||||
workspace: MOCK_WORKSPACE,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
workspaceId: MOCK_WORKSPACE.id,
|
||||
setWorkspaceId: vi.fn(),
|
||||
refetch: vi.fn(),
|
||||
children: [{ id: MOCK_CHILD.id, name: MOCK_CHILD.name, birth_date: MOCK_CHILD.birthDate }],
|
||||
selectedChildId: MOCK_CHILD.id,
|
||||
selectChild: vi.fn(),
|
||||
personalDataUuid: MOCK_PARENT_A.uuid,
|
||||
parentName: MOCK_PARENT_A.name,
|
||||
refreshContext: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeAuthContextValue(
|
||||
overrides: Partial<ReturnType<typeof useAuth>> = {},
|
||||
): ReturnType<typeof useAuth> {
|
||||
return {
|
||||
accessToken: 'token-123',
|
||||
isAuthenticated: true,
|
||||
isLoading: false,
|
||||
login: vi.fn(),
|
||||
register: vi.fn(),
|
||||
logout: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Data-fetching hooks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function makeAnalyticsResult(
|
||||
overrides: Partial<ReturnType<typeof useAnalytics>> = {},
|
||||
): ReturnType<typeof useAnalytics> {
|
||||
return {
|
||||
data: makeWorkspaceOverview(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeParentalLeaveResult(
|
||||
overrides: Partial<ReturnType<typeof useParentalLeave>> = {},
|
||||
): ReturnType<typeof useParentalLeave> {
|
||||
return {
|
||||
data: makeParentalLeaveFetch(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makePlannedDaysResult(
|
||||
overrides: Partial<ReturnType<typeof usePlannedDays>> = {},
|
||||
): ReturnType<typeof usePlannedDays> {
|
||||
return {
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
add: vi.fn(),
|
||||
update: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeScraperFetchResult(
|
||||
overrides: Partial<ReturnType<typeof useScraperFetch>> = {},
|
||||
): ReturnType<typeof useScraperFetch> {
|
||||
return {
|
||||
sessionId: null,
|
||||
pollData: null,
|
||||
resolvedIds: null,
|
||||
error: null,
|
||||
isUnauthorized: false,
|
||||
start: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
isRunning: false,
|
||||
isTerminal: false,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeWorkspaceMembersResult(
|
||||
overrides: Partial<ReturnType<typeof useWorkspaceMembers>> = {},
|
||||
): ReturnType<typeof useWorkspaceMembers> {
|
||||
return {
|
||||
data: [MOCK_PARENT_A],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeOptimizationTipsResult(
|
||||
overrides: Partial<ReturnType<typeof useOptimizationTips>> = {},
|
||||
): ReturnType<typeof useOptimizationTips> {
|
||||
return {
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
dismiss: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeDayOverridesResult(
|
||||
overrides: Partial<ReturnType<typeof useDayOverrides>> = {},
|
||||
): ReturnType<typeof useDayOverrides> {
|
||||
return {
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
upsert: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeLeaveWishesResult(
|
||||
overrides: Partial<ReturnType<typeof useLeaveWishes>> = {},
|
||||
): ReturnType<typeof useLeaveWishes> {
|
||||
return {
|
||||
data: [],
|
||||
isLoading: false,
|
||||
isRefreshing: false,
|
||||
error: null,
|
||||
refresh: vi.fn(),
|
||||
add: vi.fn(),
|
||||
update: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makePlanProposalResult(
|
||||
overrides: Partial<ReturnType<typeof usePlanProposal>> = {},
|
||||
): ReturnType<typeof usePlanProposal> {
|
||||
return {
|
||||
data: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
propose: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makePlanApplyResult(
|
||||
overrides: Partial<ReturnType<typeof usePlanApply>> = {},
|
||||
): ReturnType<typeof usePlanApply> {
|
||||
return {
|
||||
response: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
apply: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeVacationDaysResult(
|
||||
overrides: Partial<ReturnType<typeof useVacationDays>> = {},
|
||||
): ReturnType<typeof useVacationDays> {
|
||||
return {
|
||||
data: [MOCK_VACATION_DAYS],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
upsert: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeChildSettingsResult(
|
||||
overrides: Partial<ReturnType<typeof useChildSettings>> = {},
|
||||
): ReturnType<typeof useChildSettings> {
|
||||
return {
|
||||
data: MOCK_CHILD,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
update: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
export function makeCalendarDaysResult(
|
||||
overrides: Partial<ReturnType<typeof useCalendarDays>> = {},
|
||||
): ReturnType<typeof useCalendarDays> {
|
||||
return {
|
||||
data: makeCalendarWindow(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
refetch: vi.fn(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Local-storage-backed preference hook (a tuple, not an object)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function makeShowWeekNumbersTuple(
|
||||
show = false,
|
||||
setShow: (show: boolean) => void = vi.fn(),
|
||||
): ReturnType<typeof useShowWeekNumbers> {
|
||||
return [show, setShow]
|
||||
}
|
||||
Reference in New Issue
Block a user