@@ -91,6 +91,26 @@ vi.mock("@roo/array", () => ({
9191 } ,
9292} ) )
9393
94+ // Create a variable to hold the mock model info for useSelectedModel
95+ let mockModelInfo : { contextWindow : number ; maxTokens : number } | undefined = undefined
96+
97+ // Mock useSelectedModel hook
98+ vi . mock ( "@/components/ui/hooks/useSelectedModel" , ( ) => ( {
99+ useSelectedModel : ( ) => ( {
100+ provider : "anthropic" ,
101+ id : "test-model" ,
102+ info : mockModelInfo ,
103+ isLoading : false ,
104+ isError : false ,
105+ } ) ,
106+ } ) )
107+
108+ // Mock getModelMaxOutputTokens from @roo /api
109+ let mockMaxOutputTokens = 0
110+ vi . mock ( "@roo/api" , ( ) => ( {
111+ getModelMaxOutputTokens : ( ) => mockMaxOutputTokens ,
112+ } ) )
113+
94114describe ( "TaskHeader" , ( ) => {
95115 const defaultProps : TaskHeaderProps = {
96116 task : { type : "say" , ts : Date . now ( ) , text : "Test task" , images : [ ] } ,
@@ -402,4 +422,52 @@ describe("TaskHeader", () => {
402422 expect ( backButton ?. querySelector ( "svg.lucide-arrow-left" ) ) . toBeInTheDocument ( )
403423 } )
404424 } )
425+
426+ describe ( "Context window percentage calculation" , ( ) => {
427+ // The percentage should be calculated as:
428+ // contextTokens / (contextWindow - reservedForOutput) * 100
429+ // This represents the percentage of AVAILABLE input space used,
430+ // not the percentage of the total context window.
431+
432+ beforeEach ( ( ) => {
433+ // Set up mock model with known contextWindow
434+ mockModelInfo = { contextWindow : 1000 , maxTokens : 200 }
435+ // Set up mock for getModelMaxOutputTokens to return reservedForOutput
436+ mockMaxOutputTokens = 200
437+ } )
438+
439+ afterEach ( ( ) => {
440+ // Reset mocks
441+ mockModelInfo = undefined
442+ mockMaxOutputTokens = 0
443+ } )
444+
445+ it ( "should calculate percentage based on available input space, not total context window" , ( ) => {
446+ // With the formula: contextTokens / (contextWindow - reservedForOutput) * 100
447+ // If contextTokens = 200, contextWindow = 1000, reservedForOutput = 200
448+ // Then available input space = 1000 - 200 = 800
449+ // Percentage = 200 / 800 * 100 = 25%
450+ //
451+ // Old (incorrect) formula would have been: (200 + 200) / 1000 * 100 = 40%
452+
453+ renderTaskHeader ( { contextTokens : 200 } )
454+
455+ // The percentage should be rendered in the collapsed header state
456+ // Verify that 25% is displayed (correct formula) and NOT 40% (old incorrect formula)
457+ expect ( screen . getByText ( "25%" ) ) . toBeInTheDocument ( )
458+ expect ( screen . queryByText ( "40%" ) ) . not . toBeInTheDocument ( )
459+ } )
460+
461+ it ( "should handle edge case when available input space is zero" , ( ) => {
462+ // When contextWindow equals reservedForOutput, available space is 0
463+ // The percentage should be 0 to avoid division by zero
464+ mockModelInfo = { contextWindow : 200 , maxTokens : 200 }
465+ mockMaxOutputTokens = 200
466+
467+ renderTaskHeader ( { contextTokens : 100 } )
468+
469+ // Should show 0% when available input space is 0
470+ expect ( screen . getByText ( "0%" ) ) . toBeInTheDocument ( )
471+ } )
472+ } )
405473} )
0 commit comments