import React, { act } from 'react'; import { createRoot } from 'react-dom/client'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { TokenUsageDisplay } from '../../../../src/renderer/components/common/TokenUsageDisplay'; import type { ContextStats } from '../../../../src/renderer/types/contextInjection'; const contextStats: ContextStats = { newInjections: [], accumulatedInjections: [ { id: 'claude-md-1', category: 'claude-md', path: '/workspace/CLAUDE.md', source: 'project-local', displayName: 'CLAUDE.md', isGlobal: false, estimatedTokens: 200, firstSeenInGroup: 'ai-0', }, { id: 'mentioned-file-1', category: 'mentioned-file', path: '/workspace/file.ts', displayName: 'file.ts', estimatedTokens: 300, firstSeenTurnIndex: 0, firstSeenInGroup: 'ai-0', exists: true, }, ], totalEstimatedTokens: 500, tokensByCategory: { claudeMd: 200, mentionedFiles: 300, toolOutputs: 0, thinkingText: 0, taskCoordination: 0, userMessages: 0, }, newCounts: { claudeMd: 0, mentionedFiles: 0, toolOutputs: 0, thinkingText: 0, taskCoordination: 0, userMessages: 0, }, }; async function flushReact(): Promise { await Promise.resolve(); await Promise.resolve(); } describe('TokenUsageDisplay', () => { afterEach(() => { document.body.innerHTML = ''; vi.restoreAllMocks(); }); it('keeps visible context scoped to prompt input instead of context window semantics', async () => { vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); const host = document.createElement('div'); document.body.appendChild(host); const root = createRoot(host); await act(async () => { root.render( React.createElement(TokenUsageDisplay, { inputTokens: 1000, cacheReadTokens: 500, cacheCreationTokens: 500, outputTokens: 250, contextStats, }) ); await flushReact(); }); const trigger = host.querySelector('[aria-haspopup="true"]'); expect(trigger).toBeInstanceOf(HTMLElement); await act(async () => { trigger?.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); await flushReact(); }); const popover = document.querySelector('[role="tooltip"]'); expect(popover).toBeTruthy(); expect(popover?.textContent).toContain('2,250'); expect(popover?.textContent).toContain('500 (25.0% of prompt input)'); expect(popover?.textContent).not.toContain('of context'); const visibleContextToggle = Array.from(document.querySelectorAll('[role="button"]')).find( (element) => element.textContent?.includes('Visible Context') ); expect(visibleContextToggle).toBeTruthy(); await act(async () => { visibleContextToggle?.dispatchEvent(new MouseEvent('click', { bubbles: true })); await flushReact(); }); expect(popover?.textContent).toContain('CLAUDE.md ×1'); expect(popover?.textContent).toContain('(10.0%)'); expect(popover?.textContent).toContain('@files ×1'); expect(popover?.textContent).toContain('(15.0%)'); await act(async () => { root.unmount(); await flushReact(); }); }); });