MeshiTrack/docs/web_test_coverage_plan.md

114 lines
6.5 KiB
Markdown
Raw Normal View History

2026-05-19 14:09:03 +09:00
# MeshiTrack Web Test Coverage Implementation Plan
This blueprint outlines the systematic plan to bridge the remaining test coverage gaps in `@meshitrack/web` (currently at **94.45% Line / 83.61% Branch** coverage). By resolving these minor gaps, we will achieve a pristine, 100%-validated web architecture, mirroring the excellence of `@meshitrack/api` and `@meshitrack/shared`.
---
## 📊 Current State Analysis & Gap Identification
Our recent monorepo health check has pinpointed a highly consistent pattern of minor coverage gaps. Almost all uncovered branches stem from optional parameter handling (falsy pathways) and network boundary error tolerances.
```mermaid
graph TD
A[Web Coverage Gaps] --> B[1. Core API Services - 99.28%]
A --> C[2. Real-Time WebSocket - 97.82%]
A --> D[3. Interactive UI & Layout - 94.87%]
A --> E[4. Next.js Pages - 90-98%]
B --> B1[Falsy Query Parameters & Auth headers]
C --> C1[Early returns, Max reconnect attempts, Falsy event reasons]
D --> D1[Dynamic avatar falls, Toggle states, Sidebar active routes]
E --> E1[Empty lists, Loading fallbacks, Pricing permutations]
```
---
## 🛠️ Step-by-Step Execution Plan
### Phase 1: Core API Services & Client (Target: 100% Coverage)
The API services layer has reached **99.28%** coverage. We will close the final **0.72%** gap by testing standard fallback branches for optional query objects.
#### 1.1 `services/api-client.ts` (Current: 93.10% Lines / 92.85% Branches)
* **Gap**: The `.baseUrl` getter is never called, and `apiClient.delete` is never tested with an authenticated session token.
* **TDD Solution**:
* Write a test asserting that `apiClient.baseUrl` matches the configured environment URL.
* Write an integration test for `apiClient.delete` verifying that the `Authorization` header is correctly injected when `apiClient.accessToken` is set.
#### 1.2 `services/purchases.ts` (Current: 93.75% Statements / 90.00% Branches)
* **Gap**: The `cursor` query parameter on line 24 is never passed or verified in search results.
* **TDD Solution**:
* Expand `listPurchases builds query string` test in `purchases.test.ts` to include a `{ cursor: 'curr-123' }` argument, expecting `cursor=curr-123` in the generated endpoint path.
#### 1.3 `services/refills.ts` (Current: 92.85% Branches)
* **Gap**: `listRefillLists` is never tested without its optional query parameter, leaving the falsy `query` coalescing branch uncovered.
* **TDD Solution**:
* Add a unit test `listRefillLists with no query` in `refills.test.ts` asserting that the query string is omitted entirely when no arguments are provided.
#### 1.4 `services/shopping-lists.ts` (Current: 75.00% Branches)
* **Gap**: `getShoppingListSyncSocketUrl` is never tested with an empty/falsy `apiClient.baseUrl`, leaving the default string fallback branch untested.
* **TDD Solution**:
* Add a test setting `apiClient.baseUrl = ''` and verify the socket URL resolves cleanly to `ws:///households/...`.
#### 1.5 `services/medicines.ts` & `services/cabinet-events.ts`
* **Gap**: `listMedicineProducts` and `getEventsByItem` are never tested without query objects.
* **TDD Solution**:
* Add clean unit tests verifying that both functions omit parameters entirely when passed only their mandatory IDs.
---
### Phase 2: Collaborative Real-Time Sync (Target: 100% Coverage)
The custom WebSocket hook `useShoppingListSync.ts` handles collaborative syncing. It has four remaining uncovered branches on lines 22, 56-59, and 86.
```typescript
// useShoppingListSync.ts Uncovered Branches
if (!householdId || !listId) return; // 1. Early Return
ws.onclose = (event) => {
console.log(`🔌 Sync severed: ${event.reason || 'Disconnected'}`); // 2. Falsy Event Reason
if (reconnectAttemptsRef.current < 5) { ... } // 3. Exhausted Reconnect boundary
};
if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) { ... } // 4. Closed Socket Send Attempts
```
#### TDD Solution:
1. **Early Return**: Write a hook unit test passing an empty string `""` for `listId` and assert that the hook does not instantiate a WebSocket connection.
2. **Falsy Event Reason**: Close the mocked WebSocket connection without providing a closure reason, and assert that the hook safely logs `'Disconnected'` without runtime crashes.
3. **Exhausted Reconnect**: Mock 5 consecutive WebSocket connection closures, advance the test timers using `vi.useFakeTimers()`, and assert that the hook ceases to make a 6th reconnection handshake.
4. **Closed Socket Send**: Invoke `toggleItemCheck` when the mocked socket is in `WebSocket.CLOSED` state, and verify that the hook skips calling `.send` entirely.
---
### Phase 3: Interactive UI Components (Target: 100% Coverage)
Interactive UI helpers (`Avatar.tsx`, `Icon.tsx`, `Sidebar.tsx`, `TopBar.tsx`) are currently at **94.87%** branch coverage. We will exercise the rare edge cases where layout values are undefined.
* **Avatar**: Test rendering the component without an image source or name, ensuring it falls back safely to a default generic avatar layout.
* **Icon**: Pass an unknown/invalid icon name to verify the component fails gracefully or falls back to a clean default glyph rather than throwing a crash.
* **Sidebar / TopBar**: Assert active routes and toggle hamburger menu states when users reside on nested directories vs. core root settings.
---
### Phase 4: Next.js Dashboard Pages (Target: >95% Coverage)
Page-level integration tests (`shopping-lists/page.tsx`, `[id]/page.tsx`, `prices/page.tsx`, `stores/page.tsx`) will be upgraded to assert full application layouts:
* **Empty States**: Render the page with an empty list array resolved from MSW, and assert that a descriptive "No shopping lists found" prompt is present.
* **Loading Boundaries**: Capture SWR's `isValidating` or `isLoading` states, and assert that a skeleton card loader is visible.
* **Failure Interception**: Force MSW to return a 500 error, and assert that the error notification banner renders correctly to prevent silent UI failures.
---
## 🚀 TDD Feedback Loop
To make this execution fast, smooth, and interactive, we will use our customized watch command. This enables instant feedback when writing the new tests:
```bash
# Watch the web package continuously
npm run test:watch -w packages/web
# Focus specifically on one service test during Phase 1
npm run test:watch -w packages/web -- tests/services/shopping-lists.test.ts
```
> [!TIP]
> Resolving these specific gaps will yield a robust frontend architecture. This coverage guarantees that subsequent visual designs and functional refactors can be committed safely with absolute confidence.