Skip to content

Commit 7fab2f7

Browse files
authored
feat: Cost Display in Task Header - Suppress Zero Cost Values and Ensure Visibility for Gemini, OpenAI, LM Studio, and Ollama (#2662)
test: Add unit tests for TaskHeader component cost display logic
1 parent da2ab6e commit 7fab2f7

File tree

2 files changed

+116
-7
lines changed

2 files changed

+116
-7
lines changed

webview-ui/src/components/chat/TaskHeader.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,8 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
122122
}, [task.text, windowWidth])
123123

124124
const isCostAvailable = useMemo(() => {
125-
return (
126-
apiConfiguration?.apiProvider !== "openai" &&
127-
apiConfiguration?.apiProvider !== "ollama" &&
128-
apiConfiguration?.apiProvider !== "lmstudio" &&
129-
apiConfiguration?.apiProvider !== "gemini"
130-
)
131-
}, [apiConfiguration?.apiProvider])
125+
return totalCost !== null && totalCost !== undefined && totalCost > 0 && !isNaN(totalCost)
126+
}, [totalCost])
132127

133128
const shouldShowPromptCacheInfo = doesModelSupportPromptCache && apiConfiguration?.apiProvider !== "openrouter"
134129

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import React from "react"
2+
import { render, screen } from "@testing-library/react"
3+
import TaskHeader from "../TaskHeader"
4+
import { ApiConfiguration } from "../../../../../src/shared/api"
5+
6+
// Mock the vscode API
7+
jest.mock("@/utils/vscode", () => ({
8+
vscode: {
9+
postMessage: jest.fn(),
10+
},
11+
}))
12+
13+
// Mock the ExtensionStateContext
14+
jest.mock("../../../context/ExtensionStateContext", () => ({
15+
useExtensionState: () => ({
16+
apiConfiguration: {
17+
apiProvider: "anthropic",
18+
apiKey: "test-api-key", // Add relevant fields
19+
apiModelId: "claude-3-opus-20240229", // Add relevant fields
20+
} as ApiConfiguration, // Optional: Add type assertion if ApiConfiguration is imported
21+
currentTaskItem: null,
22+
}),
23+
}))
24+
25+
describe("TaskHeader", () => {
26+
const defaultProps = {
27+
task: { text: "Test task", images: [] },
28+
tokensIn: 100,
29+
tokensOut: 50,
30+
doesModelSupportPromptCache: true,
31+
totalCost: 0.05,
32+
contextTokens: 200,
33+
onClose: jest.fn(),
34+
}
35+
36+
it("should display cost when totalCost is greater than 0", () => {
37+
render(
38+
<TaskHeader
39+
{...defaultProps}
40+
task={{
41+
type: "say",
42+
ts: Date.now(),
43+
text: "Test task",
44+
images: [],
45+
}}
46+
/>,
47+
)
48+
expect(screen.getByText("$0.0500")).toBeInTheDocument()
49+
})
50+
51+
it("should not display cost when totalCost is 0", () => {
52+
render(
53+
<TaskHeader
54+
{...defaultProps}
55+
totalCost={0}
56+
task={{
57+
type: "say",
58+
ts: Date.now(),
59+
text: "Test task",
60+
images: [],
61+
}}
62+
/>,
63+
)
64+
expect(screen.queryByText("$0.0000")).not.toBeInTheDocument()
65+
})
66+
67+
it("should not display cost when totalCost is null", () => {
68+
render(
69+
<TaskHeader
70+
{...defaultProps}
71+
totalCost={null as any}
72+
task={{
73+
type: "say",
74+
ts: Date.now(),
75+
text: "Test task",
76+
images: [],
77+
}}
78+
/>,
79+
)
80+
expect(screen.queryByText(/\$/)).not.toBeInTheDocument()
81+
})
82+
83+
it("should not display cost when totalCost is undefined", () => {
84+
render(
85+
<TaskHeader
86+
{...defaultProps}
87+
totalCost={undefined as any}
88+
task={{
89+
type: "say",
90+
ts: Date.now(),
91+
text: "Test task",
92+
images: [],
93+
}}
94+
/>,
95+
)
96+
expect(screen.queryByText(/\$/)).not.toBeInTheDocument()
97+
})
98+
99+
it("should not display cost when totalCost is NaN", () => {
100+
render(
101+
<TaskHeader
102+
{...defaultProps}
103+
totalCost={NaN}
104+
task={{
105+
type: "say",
106+
ts: Date.now(),
107+
text: "Test task",
108+
images: [],
109+
}}
110+
/>,
111+
)
112+
expect(screen.queryByText(/\$/)).not.toBeInTheDocument()
113+
})
114+
})

0 commit comments

Comments
 (0)