@@ -27,6 +27,11 @@ jest.mock('react-redux', () => ({
27
27
useDispatch : jest . fn ( ) ,
28
28
} ) ) ;
29
29
30
+ jest . mock ( 'lodash' , ( ) => ( {
31
+ ...jest . requireActual ( 'lodash' ) ,
32
+ throttle : jest . fn ( ( fn ) => fn ) ,
33
+ } ) ) ;
34
+
30
35
jest . mock ( './useLoadBearingHook' , ( ) => jest . fn ( ) ) ;
31
36
32
37
jest . mock ( '@edx/frontend-platform/logging' , ( ) => ( {
@@ -61,7 +66,10 @@ const dispatch = jest.fn();
61
66
useDispatch . mockReturnValue ( dispatch ) ;
62
67
63
68
const postMessage = jest . fn ( ) ;
64
- const frame = { contentWindow : { postMessage } } ;
69
+ const frame = {
70
+ contentWindow : { postMessage } ,
71
+ getBoundingClientRect : jest . fn ( ( ) => ( { top : 100 } ) ) ,
72
+ } ;
65
73
const mockGetElementById = jest . fn ( ( ) => frame ) ;
66
74
const testHash = '#test-hash' ;
67
75
@@ -84,6 +92,10 @@ describe('useIFrameBehavior hook', () => {
84
92
beforeEach ( ( ) => {
85
93
jest . clearAllMocks ( ) ;
86
94
state . mock ( ) ;
95
+ global . document . getElementById = mockGetElementById ;
96
+ global . window . addEventListener = jest . fn ( ) ;
97
+ global . window . removeEventListener = jest . fn ( ) ;
98
+ global . window . innerHeight = 800 ;
87
99
} ) ;
88
100
afterEach ( ( ) => {
89
101
state . resetVals ( ) ;
@@ -262,6 +274,53 @@ describe('useIFrameBehavior hook', () => {
262
274
} ) ;
263
275
} ) ;
264
276
} ) ;
277
+ describe ( 'visibility tracking' , ( ) => {
278
+ it ( 'sets up visibility tracking after iframe has loaded' , ( ) => {
279
+ state . mockVals ( { ...defaultStateVals , hasLoaded : true } ) ;
280
+ useIFrameBehavior ( props ) ;
281
+
282
+ const effects = getEffects ( [ true , props . elementId ] , React ) ;
283
+ expect ( effects . length ) . toEqual ( 2 ) ;
284
+ effects [ 0 ] ( ) ; // Execute the visibility tracking effect.
285
+
286
+ expect ( global . window . addEventListener ) . toHaveBeenCalledTimes ( 2 ) ;
287
+ expect ( global . window . addEventListener ) . toHaveBeenCalledWith ( 'scroll' , expect . any ( Function ) ) ;
288
+ expect ( global . window . addEventListener ) . toHaveBeenCalledWith ( 'resize' , expect . any ( Function ) ) ;
289
+ // Initial visibility update.
290
+ expect ( postMessage ) . toHaveBeenCalledWith (
291
+ {
292
+ type : 'unit.visibilityStatus' ,
293
+ data : {
294
+ topPosition : 100 ,
295
+ viewportHeight : 800 ,
296
+ } ,
297
+ } ,
298
+ config . LMS_BASE_URL ,
299
+ ) ;
300
+ } ) ;
301
+ it ( 'does not set up visibility tracking before iframe has loaded' , ( ) => {
302
+ state . mockVals ( { ...defaultStateVals , hasLoaded : false } ) ;
303
+ useIFrameBehavior ( props ) ;
304
+
305
+ const effects = getEffects ( [ false , props . elementId ] , React ) ;
306
+ expect ( effects ) . toBeNull ( ) ;
307
+
308
+ expect ( global . window . addEventListener ) . not . toHaveBeenCalled ( ) ;
309
+ expect ( postMessage ) . not . toHaveBeenCalled ( ) ;
310
+ } ) ;
311
+ it ( 'cleans up event listeners on unmount' , ( ) => {
312
+ state . mockVals ( { ...defaultStateVals , hasLoaded : true } ) ;
313
+ useIFrameBehavior ( props ) ;
314
+
315
+ const effects = getEffects ( [ true , props . elementId ] , React ) ;
316
+ const cleanup = effects [ 0 ] ( ) ; // Execute the effect and get the cleanup function.
317
+ cleanup ( ) ; // Call the cleanup function.
318
+
319
+ expect ( global . window . removeEventListener ) . toHaveBeenCalledTimes ( 2 ) ;
320
+ expect ( global . window . removeEventListener ) . toHaveBeenCalledWith ( 'scroll' , expect . any ( Function ) ) ;
321
+ expect ( global . window . removeEventListener ) . toHaveBeenCalledWith ( 'resize' , expect . any ( Function ) ) ;
322
+ } ) ;
323
+ } ) ;
265
324
} ) ;
266
325
describe ( 'output' , ( ) => {
267
326
describe ( 'handleIFrameLoad' , ( ) => {
0 commit comments