agent-ecosystem/test/renderer/components/common/TokenUsageDisplay.test.ts

121 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<void> {
await Promise.resolve();
await Promise.resolve();
}
function withoutNumberGroupSeparators(value: string | null | undefined): string {
return (value ?? '').replace(/[\s,\u00a0\u202f]/g, '');
}
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(withoutNumberGroupSeparators(popover?.textContent)).toContain('2250');
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();
});
});
});