6.5 KiB
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.
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
.baseUrlgetter is never called, andapiClient.deleteis never tested with an authenticated session token. - TDD Solution:
- Write a test asserting that
apiClient.baseUrlmatches the configured environment URL. - Write an integration test for
apiClient.deleteverifying that theAuthorizationheader is correctly injected whenapiClient.accessTokenis set.
- Write a test asserting that
1.2 services/purchases.ts (Current: 93.75% Statements / 90.00% Branches)
- Gap: The
cursorquery parameter on line 24 is never passed or verified in search results. - TDD Solution:
- Expand
listPurchases builds query stringtest inpurchases.test.tsto include a{ cursor: 'curr-123' }argument, expectingcursor=curr-123in the generated endpoint path.
- Expand
1.3 services/refills.ts (Current: 92.85% Branches)
- Gap:
listRefillListsis never tested without its optional query parameter, leaving the falsyquerycoalescing branch uncovered. - TDD Solution:
- Add a unit test
listRefillLists with no queryinrefills.test.tsasserting that the query string is omitted entirely when no arguments are provided.
- Add a unit test
1.4 services/shopping-lists.ts (Current: 75.00% Branches)
- Gap:
getShoppingListSyncSocketUrlis never tested with an empty/falsyapiClient.baseUrl, leaving the default string fallback branch untested. - TDD Solution:
- Add a test setting
apiClient.baseUrl = ''and verify the socket URL resolves cleanly tows:///households/....
- Add a test setting
1.5 services/medicines.ts & services/cabinet-events.ts
- Gap:
listMedicineProductsandgetEventsByItemare 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.
// 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:
- Early Return: Write a hook unit test passing an empty string
""forlistIdand assert that the hook does not instantiate a WebSocket connection. - Falsy Event Reason: Close the mocked WebSocket connection without providing a closure reason, and assert that the hook safely logs
'Disconnected'without runtime crashes. - 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. - Closed Socket Send: Invoke
toggleItemCheckwhen the mocked socket is inWebSocket.CLOSEDstate, and verify that the hook skips calling.sendentirely.
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
isValidatingorisLoadingstates, 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:
# 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.