11// @vitest -environment jsdom
2- import { fireEvent , render , screen } from "@testing-library/react" ;
3- import { describe , expect , it , vi } from "vitest" ;
2+ import { cleanup , fireEvent , render , screen , within } from "@testing-library/react" ;
3+ import { afterEach , describe , expect , it , vi } from "vitest" ;
44import { Home } from "./Home" ;
55
6+ afterEach ( ( ) => {
7+ cleanup ( ) ;
8+ } ) ;
9+
610const baseProps = {
711 onOpenSettings : vi . fn ( ) ,
812 onAddWorkspace : vi . fn ( ) ,
@@ -18,6 +22,10 @@ const baseProps = {
1822 usageWorkspaceId : null ,
1923 usageWorkspaceOptions : [ ] ,
2024 onUsageWorkspaceChange : vi . fn ( ) ,
25+ accountRateLimits : null ,
26+ usageShowRemaining : false ,
27+ accountInfo : null ,
28+ accountWorkspaceLabel : null ,
2129 onSelectThread : vi . fn ( ) ,
2230} ;
2331
@@ -99,5 +107,229 @@ describe("Home", () => {
99107 expect ( screen . getAllByText ( "agent time" ) . length ) . toBeGreaterThan ( 0 ) ;
100108 expect ( screen . getByText ( "Runs" ) ) . toBeTruthy ( ) ;
101109 expect ( screen . getByText ( "Peak day" ) ) . toBeTruthy ( ) ;
110+ expect ( screen . getByText ( "Avg / run" ) ) . toBeTruthy ( ) ;
111+ expect ( screen . getByText ( "Avg / active day" ) ) . toBeTruthy ( ) ;
112+ expect ( screen . getByText ( "Longest streak" ) ) . toBeTruthy ( ) ;
113+ expect ( screen . getByText ( "Active days" ) ) . toBeTruthy ( ) ;
114+ } ) ;
115+
116+ it ( "renders expanded token stats and account limits" , ( ) => {
117+ render (
118+ < Home
119+ { ...baseProps }
120+ localUsageSnapshot = { {
121+ updatedAt : Date . now ( ) ,
122+ days : [
123+ {
124+ day : "2026-01-07" ,
125+ inputTokens : 20 ,
126+ cachedInputTokens : 5 ,
127+ outputTokens : 10 ,
128+ totalTokens : 30 ,
129+ agentTimeMs : 60000 ,
130+ agentRuns : 1 ,
131+ } ,
132+ {
133+ day : "2026-01-08" ,
134+ inputTokens : 10 ,
135+ cachedInputTokens : 0 ,
136+ outputTokens : 5 ,
137+ totalTokens : 15 ,
138+ agentTimeMs : 0 ,
139+ agentRuns : 0 ,
140+ } ,
141+ {
142+ day : "2026-01-09" ,
143+ inputTokens : 0 ,
144+ cachedInputTokens : 0 ,
145+ outputTokens : 0 ,
146+ totalTokens : 0 ,
147+ agentTimeMs : 0 ,
148+ agentRuns : 0 ,
149+ } ,
150+ {
151+ day : "2026-01-10" ,
152+ inputTokens : 0 ,
153+ cachedInputTokens : 0 ,
154+ outputTokens : 0 ,
155+ totalTokens : 0 ,
156+ agentTimeMs : 0 ,
157+ agentRuns : 0 ,
158+ } ,
159+ {
160+ day : "2026-01-11" ,
161+ inputTokens : 0 ,
162+ cachedInputTokens : 0 ,
163+ outputTokens : 0 ,
164+ totalTokens : 0 ,
165+ agentTimeMs : 0 ,
166+ agentRuns : 0 ,
167+ } ,
168+ {
169+ day : "2026-01-12" ,
170+ inputTokens : 0 ,
171+ cachedInputTokens : 0 ,
172+ outputTokens : 0 ,
173+ totalTokens : 0 ,
174+ agentTimeMs : 0 ,
175+ agentRuns : 0 ,
176+ } ,
177+ {
178+ day : "2026-01-13" ,
179+ inputTokens : 30 ,
180+ cachedInputTokens : 10 ,
181+ outputTokens : 20 ,
182+ totalTokens : 50 ,
183+ agentTimeMs : 120000 ,
184+ agentRuns : 2 ,
185+ } ,
186+ {
187+ day : "2026-01-14" ,
188+ inputTokens : 35 ,
189+ cachedInputTokens : 10 ,
190+ outputTokens : 15 ,
191+ totalTokens : 50 ,
192+ agentTimeMs : 120000 ,
193+ agentRuns : 2 ,
194+ } ,
195+ {
196+ day : "2026-01-15" ,
197+ inputTokens : 25 ,
198+ cachedInputTokens : 5 ,
199+ outputTokens : 15 ,
200+ totalTokens : 40 ,
201+ agentTimeMs : 120000 ,
202+ agentRuns : 2 ,
203+ } ,
204+ {
205+ day : "2026-01-16" ,
206+ inputTokens : 15 ,
207+ cachedInputTokens : 5 ,
208+ outputTokens : 10 ,
209+ totalTokens : 25 ,
210+ agentTimeMs : 60000 ,
211+ agentRuns : 1 ,
212+ } ,
213+ {
214+ day : "2026-01-17" ,
215+ inputTokens : 0 ,
216+ cachedInputTokens : 0 ,
217+ outputTokens : 0 ,
218+ totalTokens : 0 ,
219+ agentTimeMs : 0 ,
220+ agentRuns : 0 ,
221+ } ,
222+ {
223+ day : "2026-01-18" ,
224+ inputTokens : 20 ,
225+ cachedInputTokens : 8 ,
226+ outputTokens : 12 ,
227+ totalTokens : 32 ,
228+ agentTimeMs : 90000 ,
229+ agentRuns : 1 ,
230+ } ,
231+ {
232+ day : "2026-01-19" ,
233+ inputTokens : 40 ,
234+ cachedInputTokens : 10 ,
235+ outputTokens : 25 ,
236+ totalTokens : 65 ,
237+ agentTimeMs : 180000 ,
238+ agentRuns : 3 ,
239+ } ,
240+ {
241+ day : "2026-01-20" ,
242+ inputTokens : 20 ,
243+ cachedInputTokens : 4 ,
244+ outputTokens : 16 ,
245+ totalTokens : 36 ,
246+ agentTimeMs : 120000 ,
247+ agentRuns : 2 ,
248+ } ,
249+ ] ,
250+ totals : {
251+ last7DaysTokens : 248 ,
252+ last30DaysTokens : 343 ,
253+ averageDailyTokens : 35 ,
254+ cacheHitRatePercent : 25 ,
255+ peakDay : "2026-01-19" ,
256+ peakDayTokens : 65 ,
257+ } ,
258+ topModels : [ { model : "gpt-5" , tokens : 300 , sharePercent : 87.5 } ] ,
259+ } }
260+ accountRateLimits = { {
261+ primary : {
262+ usedPercent : 62 ,
263+ windowDurationMins : 300 ,
264+ resetsAt : Math . round ( Date . now ( ) / 1000 ) + 3600 ,
265+ } ,
266+ secondary : {
267+ usedPercent : 34 ,
268+ windowDurationMins : 10080 ,
269+ resetsAt : Math . round ( Date . now ( ) / 1000 ) + 86400 ,
270+ } ,
271+ credits : {
272+ hasCredits : true ,
273+ unlimited : true ,
274+ balance : null ,
275+ } ,
276+ planType : "pro" ,
277+ } }
278+ accountInfo = { {
279+ type : "chatgpt" ,
280+ email : "user@example.com" ,
281+ planType : "pro" ,
282+ requiresOpenaiAuth : false ,
283+ } }
284+ accountWorkspaceLabel = "CodexMonitor"
285+ /> ,
286+ ) ;
287+
288+ expect ( screen . getByText ( "Cached tokens" ) ) . toBeTruthy ( ) ;
289+ expect ( screen . getByText ( "Avg / run" ) ) . toBeTruthy ( ) ;
290+ expect ( screen . getByText ( "Longest streak" ) ) . toBeTruthy ( ) ;
291+ expect ( screen . getByText ( "4 days" ) ) . toBeTruthy ( ) ;
292+ expect ( screen . getByText ( "Account limits" ) ) . toBeTruthy ( ) ;
293+ expect ( screen . getByText ( "Unlimited" ) ) . toBeTruthy ( ) ;
294+ expect ( screen . getByText ( "Pro" ) ) . toBeTruthy ( ) ;
295+ expect ( screen . getByText ( / u s e r @ e x a m p l e \. c o m / ) ) . toBeTruthy ( ) ;
296+
297+ const todayCard = screen . getByText ( "Today" ) . closest ( ".home-usage-card" ) ;
298+ expect ( todayCard ) . toBeTruthy ( ) ;
299+ if ( ! ( todayCard instanceof HTMLElement ) ) {
300+ throw new Error ( "Expected today usage card" ) ;
301+ }
302+ expect ( within ( todayCard ) . getByText ( "36" ) ) . toBeTruthy ( ) ;
303+
304+ expect (
305+ screen . getByLabelText ( "Usage week 2026-01-14 to 2026-01-20" ) ,
306+ ) . toBeTruthy ( ) ;
307+ expect (
308+ ( screen . getByRole ( "button" , { name : "Show next week" } ) as HTMLButtonElement )
309+ . disabled ,
310+ ) . toBe ( true ) ;
311+ expect (
312+ screen . getByText ( "Jan 20" ) . closest ( ".home-usage-bar" ) ?. getAttribute ( "data-value" ) ,
313+ ) . toBe ( "Jan 20 · 36 tokens" ) ;
314+
315+ fireEvent . click ( screen . getByRole ( "button" , { name : "Show previous week" } ) ) ;
316+
317+ expect (
318+ screen . getByLabelText ( "Usage week 2026-01-07 to 2026-01-13" ) ,
319+ ) . toBeTruthy ( ) ;
320+ expect (
321+ ( screen . getByRole ( "button" , { name : "Show next week" } ) as HTMLButtonElement )
322+ . disabled ,
323+ ) . toBe ( false ) ;
324+
325+ fireEvent . click ( screen . getByRole ( "button" , { name : "Show next week" } ) ) ;
326+
327+ expect (
328+ screen . getByLabelText ( "Usage week 2026-01-14 to 2026-01-20" ) ,
329+ ) . toBeTruthy ( ) ;
330+ expect (
331+ ( screen . getByRole ( "button" , { name : "Show next week" } ) as HTMLButtonElement )
332+ . disabled ,
333+ ) . toBe ( true ) ;
102334 } ) ;
103335} ) ;
0 commit comments