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