2323const StatsWindow = require ( "./statistics.js" ) ;
2424
2525describe ( "StatsWindow" , ( ) => {
26- let activity , widgetWin , body , capturedCb ;
26+ let activity , widgetWin , body , capturedCb , myChartMock , radarMock ;
2727
2828 beforeEach ( ( ) => {
2929 capturedCb = null ;
@@ -58,7 +58,8 @@ describe("StatsWindow", () => {
5858 windowFor : jest . fn ( ) . mockReturnValue ( widgetWin )
5959 } ;
6060
61- global . docById = jest . fn ( ) . mockReturnValue ( { getContext : jest . fn ( ) . mockReturnValue ( { } ) } ) ;
61+ myChartMock = { getContext : jest . fn ( ) . mockReturnValue ( { } ) } ;
62+ global . docById = jest . fn ( ) . mockReturnValue ( myChartMock ) ;
6263
6364 global . analyzeProject = jest . fn ( ) . mockReturnValue ( { } ) ;
6465 global . runAnalytics = jest . fn ( ) ;
@@ -68,10 +69,11 @@ describe("StatsWindow", () => {
6869 return { } ;
6970 } ) ;
7071
72+ radarMock = {
73+ toBase64Image : jest . fn ( ) . mockReturnValue ( "data:image/png;base64,fakedata" )
74+ } ;
7175 global . Chart = jest . fn ( ) . mockImplementation ( ( ) => ( {
72- Radar : jest . fn ( ) . mockReturnValue ( {
73- toBase64Image : jest . fn ( ) . mockReturnValue ( "data:image/png;base64,fakedata" )
74- } )
76+ Radar : jest . fn ( ) . mockReturnValue ( radarMock )
7577 } ) ) ;
7678 } ) ;
7779
@@ -118,6 +120,22 @@ describe("StatsWindow", () => {
118120 expect ( body . style . padding ) . toBe ( "0px 0px" ) ;
119121 } ) ;
120122
123+ test ( "onmaximize clears existing body content before rerender" , ( ) => {
124+ const sw = new StatsWindow ( activity ) ;
125+ const staleNode = document . createElement ( "p" ) ;
126+ staleNode . textContent = "stale" ;
127+ body . appendChild ( staleNode ) ;
128+ const previousJsonObject = sw . jsonObject ;
129+ global . analyzeProject . mockClear ( ) ;
130+
131+ widgetWin . isMaximized . mockReturnValue ( false ) ;
132+ widgetWin . onmaximize ( ) ;
133+
134+ expect ( body . textContent ) . not . toContain ( "stale" ) ;
135+ expect ( sw . jsonObject ) . not . toBe ( previousJsonObject ) ;
136+ expect ( global . analyzeProject ) . toHaveBeenCalledTimes ( 1 ) ;
137+ } ) ;
138+
121139 test ( "chart callback sets img width to 200 when not maximized" , ( ) => {
122140 widgetWin . isMaximized . mockReturnValue ( false ) ;
123141 const sw = new StatsWindow ( activity ) ;
@@ -143,6 +161,65 @@ describe("StatsWindow", () => {
143161 expect ( imgs [ imgs . length - 1 ] . width ) . toBe ( 420 ) ; // 500 - 80
144162 } ) ;
145163
164+ test ( "doAnalytics initializes chart with canvas context and analytics data" , ( ) => {
165+ const chartData = { labels : [ "a" ] } ;
166+ const chartOptions = { responsive : true } ;
167+ global . scoreToChartData . mockReturnValue ( chartData ) ;
168+ global . getChartOptions . mockImplementation ( cb => {
169+ capturedCb = cb ;
170+ return chartOptions ;
171+ } ) ;
172+
173+ const sw = new StatsWindow ( activity ) ;
174+
175+ expect ( global . docById ) . toHaveBeenCalledWith ( "myChart" ) ;
176+ expect ( myChartMock . getContext ) . toHaveBeenCalledWith ( "2d" ) ;
177+ expect ( global . scoreToChartData ) . toHaveBeenCalledWith ( { } ) ;
178+ expect ( global . getChartOptions ) . toHaveBeenCalledWith ( expect . any ( Function ) ) ;
179+ expect ( global . Chart ) . toHaveBeenCalledWith ( myChartMock . getContext . mock . results [ 0 ] . value ) ;
180+ expect ( activity . blocks . activeBlock ) . toBeNull ( ) ;
181+ expect ( document . body . style . cursor ) . toBe ( "wait" ) ;
182+ expect ( activity . loading ) . toBe ( true ) ;
183+ } ) ;
184+
185+ test ( "doAnalytics appends json list container with left float style" , ( ) => {
186+ const sw = new StatsWindow ( activity ) ;
187+
188+ expect ( sw . jsonObject ) . toBeInstanceOf ( HTMLUListElement ) ;
189+ expect ( sw . jsonObject . style . float ) . toBe ( "left" ) ;
190+ expect ( body . lastChild ) . toBe ( sw . jsonObject ) ;
191+ } ) ;
192+
193+ test ( "chart callback reads base64 image from radar chart" , ( ) => {
194+ const sw = new StatsWindow ( activity ) ;
195+
196+ capturedCb ( ) ;
197+
198+ expect ( radarMock . toBase64Image ) . toHaveBeenCalled ( ) ;
199+ const imgs = body . querySelectorAll ( "img" ) ;
200+ expect ( imgs [ imgs . length - 1 ] . src ) . toContain ( "data:image/png;base64,fakedata" ) ;
201+ } ) ;
202+
203+ test ( "chart callback appends chart image after stats list" , ( ) => {
204+ const sw = new StatsWindow ( activity ) ;
205+
206+ capturedCb ( ) ;
207+
208+ expect ( body . children [ 0 ] ) . toBe ( sw . jsonObject ) ;
209+ expect ( body . children [ body . children . length - 1 ] . tagName ) . toBe ( "IMG" ) ;
210+ } ) ;
211+
212+ test ( "onmaximize reruns analytics and updates chart callback reference" , ( ) => {
213+ const sw = new StatsWindow ( activity ) ;
214+ const firstCb = capturedCb ;
215+ global . getChartOptions . mockClear ( ) ;
216+
217+ widgetWin . onmaximize ( ) ;
218+
219+ expect ( global . getChartOptions ) . toHaveBeenCalledTimes ( 1 ) ;
220+ expect ( capturedCb ) . not . toBe ( firstCb ) ;
221+ } ) ;
222+
146223 test ( "displayInfo renders all stats fields with correct Hz" , ( ) => {
147224 const sw = new StatsWindow ( activity ) ;
148225
0 commit comments