@@ -6,6 +6,7 @@ import { describe, it, expect } from 'vitest';
66import {
77 estimateContextUsage ,
88 calculateContextTokens ,
9+ calculateContextDisplay ,
910 estimateAccumulatedGrowth ,
1011 DEFAULT_CONTEXT_WINDOWS ,
1112} from '../../../renderer/utils/contextUsage' ;
@@ -348,6 +349,104 @@ describe('estimateAccumulatedGrowth', () => {
348349 } ) ;
349350} ) ;
350351
352+ describe ( 'calculateContextDisplay' , ( ) => {
353+ it ( 'should calculate tokens and percentage for normal usage' , ( ) => {
354+ const result = calculateContextDisplay (
355+ { inputTokens : 50000 , cacheReadInputTokens : 30000 , cacheCreationInputTokens : 20000 } ,
356+ 200000 ,
357+ 'claude-code'
358+ ) ;
359+ // (50000 + 30000 + 20000) / 200000 = 50%
360+ expect ( result . tokens ) . toBe ( 100000 ) ;
361+ expect ( result . percentage ) . toBe ( 50 ) ;
362+ expect ( result . contextWindow ) . toBe ( 200000 ) ;
363+ } ) ;
364+
365+ it ( 'should fall back to fallbackPercentage when tokens exceed context window' , ( ) => {
366+ const result = calculateContextDisplay (
367+ {
368+ inputTokens : 50000 ,
369+ cacheReadInputTokens : 758000 ,
370+ cacheCreationInputTokens : 200000 ,
371+ } ,
372+ 200000 ,
373+ 'claude-code' ,
374+ 75 // preserved contextUsage from session
375+ ) ;
376+ // Raw = 1008000 > 200000, so falls back: tokens = round(75/100 * 200000) = 150000
377+ expect ( result . tokens ) . toBe ( 150000 ) ;
378+ expect ( result . percentage ) . toBe ( 75 ) ;
379+ } ) ;
380+
381+ it ( 'should cap percentage at 100 when tokens are close to window' , ( ) => {
382+ const result = calculateContextDisplay (
383+ { inputTokens : 190000 , cacheReadInputTokens : 15000 , cacheCreationInputTokens : 0 } ,
384+ 200000 ,
385+ 'claude-code'
386+ ) ;
387+ // (190000 + 15000) / 200000 = 102.5% -> capped at 100%
388+ expect ( result . percentage ) . toBe ( 100 ) ;
389+ } ) ;
390+
391+ it ( 'should return zeros when context window is 0' , ( ) => {
392+ const result = calculateContextDisplay (
393+ { inputTokens : 50000 } ,
394+ 0 ,
395+ 'claude-code'
396+ ) ;
397+ expect ( result . tokens ) . toBe ( 0 ) ;
398+ expect ( result . percentage ) . toBe ( 0 ) ;
399+ expect ( result . contextWindow ) . toBe ( 0 ) ;
400+ } ) ;
401+
402+ it ( 'should not fall back when no fallbackPercentage is provided' , ( ) => {
403+ const result = calculateContextDisplay (
404+ {
405+ inputTokens : 50000 ,
406+ cacheReadInputTokens : 758000 ,
407+ cacheCreationInputTokens : 200000 ,
408+ } ,
409+ 200000 ,
410+ 'claude-code'
411+ // no fallback
412+ ) ;
413+ // Raw = 1008000 > 200000, but no fallback, so tokens stay at raw value
414+ // Percentage is capped at 100%
415+ expect ( result . tokens ) . toBe ( 1008000 ) ;
416+ expect ( result . percentage ) . toBe ( 100 ) ;
417+ } ) ;
418+
419+ it ( 'should use Codex semantics (includes output tokens)' , ( ) => {
420+ const result = calculateContextDisplay (
421+ { inputTokens : 50000 , outputTokens : 30000 , cacheCreationInputTokens : 20000 } ,
422+ 200000 ,
423+ 'codex'
424+ ) ;
425+ // Codex: (50000 + 20000 + 30000) / 200000 = 50%
426+ expect ( result . tokens ) . toBe ( 100000 ) ;
427+ expect ( result . percentage ) . toBe ( 50 ) ;
428+ } ) ;
429+
430+ it ( 'should handle history entries with accumulated tokens and preserved contextUsage' , ( ) => {
431+ // Simulates what HistoryDetailModal sees: accumulated stats + entry.contextUsage
432+ const result = calculateContextDisplay (
433+ {
434+ inputTokens : 5676 ,
435+ outputTokens : 8522 ,
436+ cacheReadInputTokens : 1128700 ,
437+ cacheCreationInputTokens : 0 ,
438+ } ,
439+ 200000 ,
440+ undefined , // history entries don't have agent type
441+ 100 // the screenshot showed 100% context
442+ ) ;
443+ // Raw = 5676 + 1128700 + 0 = 1134376 > 200000
444+ // Falls back to: round(100/100 * 200000) = 200000
445+ expect ( result . tokens ) . toBe ( 200000 ) ;
446+ expect ( result . percentage ) . toBe ( 100 ) ;
447+ } ) ;
448+ } ) ;
449+
351450describe ( 'DEFAULT_CONTEXT_WINDOWS' , ( ) => {
352451 it ( 'should have context windows defined for all ToolType agent types' , ( ) => {
353452 // Only ToolType values have context windows defined
0 commit comments