Test/expand coverage services utils#66
Conversation
…izer, ai-client) - Add snapshot-builder.test.ts (24 tests): buildSnapshot, hashSnapshot, snapshotToPromptText - Add token-optimizer.test.ts (21 tests): rate limiting, cache TTL/eviction, subscription validation - Add ai-client.test.ts (15 tests): happy path, error codes, markdown fence stripping, cache dedup - Widen vitest.config.ts coverage include to src/services/** and src/utils/** - Install @vitest/coverage-v8 dev dependency Closes hoainho#33
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive unit tests for the AI client, snapshot builder, and token optimizer services, while expanding Vitest coverage configuration to include these services. Additionally, it enhances the accessibility of the Panel component by implementing WAI-ARIA tab navigation with arrow-key roving focus and appropriate ARIA roles. Feedback on these changes identifies two accessibility improvements in Panel.tsx: conditionally setting aria-controls to undefined for inactive tabs to avoid referencing non-existent DOM elements, and removing the disruptive aria-live="polite" attribute from the tab panel.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| id={`tab-${tab.id}`} | ||
| role="tab" | ||
| aria-selected={activeTab === tab.id} | ||
| aria-controls={`panel-${tab.id}`} |
There was a problem hiding this comment.
The aria-controls attribute should refer to an element that actually exists in the DOM. Since only the active tab panel is rendered dynamically, the inactive tabs point to non-existent IDs, which violates WAI-ARIA specifications and flags accessibility validation errors. Omit or set aria-controls to undefined for inactive tabs.
| aria-controls={`panel-${tab.id}`} | |
| aria-controls={activeTab === tab.id ? 'panel-' + tab.id : undefined} |
| aria-labelledby={`tab-${activeTab}`} | ||
| aria-live="polite" | ||
| tabIndex={0} |
There was a problem hiding this comment.
The aria-live="polite" attribute on the main tabpanel is inappropriate here. Switching tabs is an explicit user action, and screen readers will already announce the selected tab. Having aria-live on the panel causes screen readers to aggressively read out the entire panel content as soon as it mounts, creating a noisy and disruptive user experience. It should be removed.
| aria-labelledby={`tab-${activeTab}`} | |
| aria-live="polite" | |
| tabIndex={0} | |
| aria-labelledby={'tab-' + activeTab}\n tabIndex={0} |
hoainho
left a comment
There was a problem hiding this comment.
Review: ✅ Looks good
Tests: 60 well-structured tests across 3 service modules:
- snapshot-builder: 24 tests covering buildSnapshot, hashSnapshot, snapshotToPromptText — edge cases for empty states, limits (50 issues, 30 components, 10 crashes), stack truncation, memory rounding
- token-optimizer: 21 tests covering rate limiting (sliding window), TTL-based cache eviction (20-entry LRU), subscription validation, token accounting
- ai-client: 15 tests covering empty snapshot guard, happy path (JSON parsing, markdown fences, dedup via cache), 7 error codes (NETWORK_ERROR, AUTH_ERROR, RATE_LIMITED, PROXY_ERROR, ABORTED, PARSE_ERROR, raw text fallback)
Accessibility: WAI-ARIA tab navigation with arrow-key roving focus, proper role attributes, tabindex management — all good additions.
Minor note: aria-live="polite" on the tab panel container may announce content changes to screen readers on every tab switch. Consider removing or using aria-live="off" if the panel content is purely user-triggered. Not a blocker.
Ready to merge.
Linked issue
Closes #33
Type of change
What changed
Added 60 tests across 3 new test files covering the previously untested service modules (
snapshot-builder,token-optimizer,ai-client). Also widenedvitest.config.tscoverageincludefromsrc/panel/**only to also includesrc/services/**andsrc/utils/**, so these modules now appear in coverage reports.Testing notes
Note: 29 pre-existing test failures exist in the repo (CLSTab, MemoryTab, SideEffectsTab, PerformanceTab, IssueCard, UIStateTab, TimelineTab). These are unrelated to this PR — they fail because those components render emojis via CSS
<span>classes rather than as text nodes, sogetByText('🔴 ...')cannot find them. All failures existed onmainbefore this branch was created.Claim confirmation
I'll take thison the issue before starting workChecklist
npm run test:runpasses locally (pre-existing failures are unrelated to this change)npm run buildsucceeds locally## [Unreleased]— skipped (test-only PR)test(services): expand coverage to snapshot-builder, token-optimizer, ai-client@vitest/coverage-v8is a dev dependency only)Additional notes
Why these three files? They are pure logic with no Chrome API or DOM dependencies, making them straightforward to test in jsdom. Yet they cover the most critical runtime paths —
hashSnapshotusescrypto.subtle(a silent regression could break AI deduplication),checkRateLimitimplements a sliding window (an off-by-one would silently let users exceed the free quota), and the AI client error paths were completely dark before this PR.Test design decisions:
token-optimizerusesvi.resetModules()per suite to get a fresh singleton instance, avoiding state leaking between testsai-clientstubsfetchglobally per test so no real network calls are madevi.useFakeTimers) are used only in tests that need to advance time (TTL expiry, sliding window reset), keeping other tests fast