@@ -55,14 +55,30 @@ const createAIAssistantView = ({
5555
5656 return undefined ;
5757 } ) ;
58+ const $columnHeadersElement = $ ( '<div>' ) . appendTo ( $container ) ;
59+ const $rowsViewElement = $ ( '<div>' ) . css ( 'height' , '400px' ) . appendTo ( $container ) ;
60+
61+ const mockColumnHeadersView = {
62+ getHeight : jest . fn ( ) . mockReturnValue ( 50 ) ,
63+ element : jest . fn ( ) . mockReturnValue ( $columnHeadersElement ) ,
64+ } ;
65+ const mockRowsView = {
66+ element : jest . fn ( ) . mockReturnValue ( $rowsViewElement ) ,
67+ } ;
68+
5869 const mockComponent = {
5970 element : ( ) : any => $container . get ( 0 ) ,
6071 _createComponent : createComponentMock ,
6172 _controllers : { } ,
73+ _views : {
74+ columnHeadersView : mockColumnHeadersView ,
75+ rowsView : mockRowsView ,
76+ } ,
6277 option : optionMock ,
6378 } ;
6479
6580 const aiAssistantView = new AIAssistantView ( mockComponent ) ;
81+ aiAssistantView . init ( ) ;
6682 if ( render ) {
6783 aiAssistantView . render ( $container ) ;
6884 }
@@ -114,13 +130,16 @@ describe('AIAssistantView', () => {
114130 expect ( AIChat ) . toHaveBeenCalledTimes ( 1 ) ;
115131 } ) ;
116132
117- it ( 'should pass container and createComponent to AIChat' , ( ) => {
133+ it ( 'should pass container, createComponent, popupOptions, chatOptions, and onChatCleared to AIChat' , ( ) => {
118134 const { aiAssistantView } = createAIAssistantView ( ) ;
119135
120136 expect ( AIChat ) . toHaveBeenCalledWith (
121137 expect . objectContaining ( {
122138 container : aiAssistantView . element ( ) ,
123139 createComponent : expect . any ( Function ) ,
140+ popupOptions : expect . any ( Object ) ,
141+ chatOptions : expect . any ( Object ) ,
142+ onChatCleared : expect . any ( Function ) ,
124143 } ) ,
125144 ) ;
126145 } ) ;
@@ -213,20 +232,158 @@ describe('AIAssistantView', () => {
213232 } ) ;
214233
215234 describe ( 'visibilityChanged' , ( ) => {
216- it ( 'should fire visibilityChanged callback when popup visibility changes ' , ( ) => {
235+ it ( 'should fire visibilityChanged callback with true when popup onShowing is triggered ' , ( ) => {
217236 const { aiAssistantView } = createAIAssistantView ( ) ;
218237 const callback = jest . fn ( ) ;
219238
220239 aiAssistantView . visibilityChanged ?. add ( callback ) ;
221240
222241 const aiChatConfig = ( AIChat as jest . Mock ) . mock . calls [ 0 ] [ 0 ] as AIChatOptions ;
223- aiChatConfig . onVisibilityChanged ?. ( true ) ;
242+ aiChatConfig . popupOptions ?. onShowing ?. ( { } as any ) ;
224243
225244 expect ( callback ) . toHaveBeenCalledWith ( true ) ;
245+ } ) ;
226246
227- aiChatConfig . onVisibilityChanged ?.( false ) ;
247+ it ( 'should fire visibilityChanged callback with false when popup onHidden is triggered' , ( ) => {
248+ const { aiAssistantView } = createAIAssistantView ( ) ;
249+ const callback = jest . fn ( ) ;
250+
251+ aiAssistantView . visibilityChanged ?. add ( callback ) ;
252+
253+ const aiChatConfig = ( AIChat as jest . Mock ) . mock . calls [ 0 ] [ 0 ] as AIChatOptions ;
254+ aiChatConfig . popupOptions ?. onHidden ?.( { } as any ) ;
228255
229256 expect ( callback ) . toHaveBeenCalledWith ( false ) ;
230257 } ) ;
231258 } ) ;
259+
260+ describe ( 'optionChanged' , ( ) => {
261+ it ( 'should set handled to true for aiAssistant options' , ( ) => {
262+ const { aiAssistantView } = createAIAssistantView ( ) ;
263+
264+ const args = {
265+ name : 'aiAssistant' as const ,
266+ fullName : 'aiAssistant.title' as const ,
267+ value : 'New Title' ,
268+ previousValue : 'Old Title' ,
269+ handled : false ,
270+ } ;
271+
272+ aiAssistantView . optionChanged ( args ) ;
273+
274+ expect ( args . handled ) . toBe ( true ) ;
275+ } ) ;
276+
277+ it ( 'should call _invalidate when aiAssistant.enabled changes to true' , ( ) => {
278+ const { aiAssistantView } = createAIAssistantView ( ) ;
279+ const invalidateSpy = jest . spyOn ( aiAssistantView , '_invalidate' as any ) ;
280+
281+ aiAssistantView . optionChanged ( {
282+ name : 'aiAssistant' as const ,
283+ fullName : 'aiAssistant.enabled' as const ,
284+ value : true ,
285+ previousValue : false ,
286+ handled : false ,
287+ } ) ;
288+
289+ expect ( invalidateSpy ) . toHaveBeenCalledTimes ( 1 ) ;
290+ } ) ;
291+
292+ it ( 'should call hide when aiAssistant.enabled changes to false' , ( ) => {
293+ const { aiAssistantView, setEnabled } = createAIAssistantView ( ) ;
294+ const hideSpy = jest . spyOn ( aiAssistantView , 'hide' ) ;
295+
296+ setEnabled ( false ) ;
297+
298+ aiAssistantView . optionChanged ( {
299+ name : 'aiAssistant' as const ,
300+ fullName : 'aiAssistant.enabled' as const ,
301+ value : false ,
302+ previousValue : true ,
303+ handled : false ,
304+ } ) ;
305+
306+ expect ( hideSpy ) . toHaveBeenCalledTimes ( 1 ) ;
307+ } ) ;
308+
309+ it ( 'should call updateOptions on aiChatInstance for title change' , ( ) => {
310+ const { aiAssistantView } = createAIAssistantView ( ) ;
311+
312+ const aiChatInstance = ( AIChat as jest . Mock )
313+ . mock . results [ 0 ] . value as { updateOptions : jest . Mock } ;
314+
315+ aiAssistantView . optionChanged ( {
316+ name : 'aiAssistant' as const ,
317+ fullName : 'aiAssistant.title' as const ,
318+ value : 'New Title' ,
319+ previousValue : 'Old Title' ,
320+ handled : false ,
321+ } ) ;
322+
323+ expect ( aiChatInstance . updateOptions ) . toHaveBeenCalledTimes ( 1 ) ;
324+ expect ( aiChatInstance . updateOptions ) . toHaveBeenCalledWith (
325+ expect . any ( Object ) ,
326+ true ,
327+ false ,
328+ ) ;
329+ } ) ;
330+
331+ it ( 'should call updateOptions on aiChatInstance for chat options change' , ( ) => {
332+ const { aiAssistantView } = createAIAssistantView ( ) ;
333+
334+ const aiChatInstance = ( AIChat as jest . Mock )
335+ . mock . results [ 0 ] . value as { updateOptions : jest . Mock } ;
336+
337+ aiAssistantView . optionChanged ( {
338+ name : 'aiAssistant' as const ,
339+ fullName : 'aiAssistant.chat' as const ,
340+ value : { speechToTextEnabled : false } ,
341+ previousValue : { } ,
342+ handled : false ,
343+ } ) ;
344+
345+ expect ( aiChatInstance . updateOptions ) . toHaveBeenCalledTimes ( 1 ) ;
346+ expect ( aiChatInstance . updateOptions ) . toHaveBeenCalledWith (
347+ expect . any ( Object ) ,
348+ false ,
349+ true ,
350+ ) ;
351+ } ) ;
352+
353+ it ( 'should call updateOptions with both flags when object value contains title and chat' , ( ) => {
354+ const { aiAssistantView } = createAIAssistantView ( ) ;
355+
356+ const aiChatInstance = ( AIChat as jest . Mock )
357+ . mock . results [ 0 ] . value as { updateOptions : jest . Mock } ;
358+
359+ aiAssistantView . optionChanged ( {
360+ name : 'aiAssistant' as const ,
361+ fullName : 'aiAssistant' as const ,
362+ value : { title : 'New title' , chat : { speechToTextEnabled : false } } ,
363+ previousValue : { title : 'Old title' } ,
364+ handled : false ,
365+ } ) ;
366+
367+ expect ( aiChatInstance . updateOptions ) . toHaveBeenCalledTimes ( 1 ) ;
368+ expect ( aiChatInstance . updateOptions ) . toHaveBeenCalledWith (
369+ expect . any ( Object ) ,
370+ true ,
371+ true ,
372+ ) ;
373+ } ) ;
374+
375+ it ( 'should not throw when aiChatInstance is not created for non-enabled sub-options' , ( ) => {
376+ const { aiAssistantView } = createAIAssistantView ( { render : false } ) ;
377+
378+ expect ( ( ) => {
379+ aiAssistantView . optionChanged ( {
380+ name : 'aiAssistant' as const ,
381+ fullName : 'aiAssistant.title' as const ,
382+ value : 'New Title' ,
383+ previousValue : 'Old Title' ,
384+ handled : false ,
385+ } ) ;
386+ } ) . not . toThrow ( ) ;
387+ } ) ;
388+ } ) ;
232389} ) ;
0 commit comments