MeshiTrack/docs/web_test_coverage_plan.md

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 .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.

// 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:

# 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.