@@ -355,4 +355,152 @@ describe("useImpressionRef", () => {
355355
356356 expect ( mockLogEvent ) . not . toHaveBeenCalled ( ) ;
357357 } ) ;
358+
359+ describe ( "with minDurationMs" , ( ) => {
360+ beforeEach ( ( ) => {
361+ jest . useFakeTimers ( ) ;
362+ } ) ;
363+
364+ afterEach ( ( ) => {
365+ jest . useRealTimers ( ) ;
366+ } ) ;
367+
368+ it ( "fires after the element is continuously visible for the duration" , ( ) => {
369+ const { result } = renderHook ( ( ) =>
370+ useImpressionRef (
371+ "card.viewed" ,
372+ { card_id : "1" } ,
373+ { minDurationMs : 250 } ,
374+ ) ,
375+ ) ;
376+
377+ const node = document . createElement ( "div" ) ;
378+ act ( ( ) => {
379+ result . current ( node ) ;
380+ } ) ;
381+
382+ act ( ( ) => {
383+ lastCallback (
384+ [ { isIntersecting : true } as IntersectionObserverEntry ] ,
385+ { } as IntersectionObserver ,
386+ ) ;
387+ } ) ;
388+
389+ expect ( mockLogEvent ) . not . toHaveBeenCalled ( ) ;
390+
391+ act ( ( ) => {
392+ jest . advanceTimersByTime ( 250 ) ;
393+ } ) ;
394+
395+ expect ( mockLogEvent ) . toHaveBeenCalledTimes ( 1 ) ;
396+ expect ( mockLogEvent ) . toHaveBeenCalledWith ( "card.viewed" , {
397+ card_id : "1" ,
398+ } ) ;
399+ expect ( mockDisconnect ) . toHaveBeenCalled ( ) ;
400+ } ) ;
401+
402+ it ( "does not fire if the element leaves before the duration elapses" , ( ) => {
403+ const { result } = renderHook ( ( ) =>
404+ useImpressionRef ( "card.viewed" , { } , { minDurationMs : 250 } ) ,
405+ ) ;
406+
407+ const node = document . createElement ( "div" ) ;
408+ act ( ( ) => {
409+ result . current ( node ) ;
410+ } ) ;
411+
412+ act ( ( ) => {
413+ lastCallback (
414+ [ { isIntersecting : true } as IntersectionObserverEntry ] ,
415+ { } as IntersectionObserver ,
416+ ) ;
417+ } ) ;
418+
419+ act ( ( ) => {
420+ jest . advanceTimersByTime ( 100 ) ;
421+ } ) ;
422+
423+ act ( ( ) => {
424+ lastCallback (
425+ [ { isIntersecting : false } as IntersectionObserverEntry ] ,
426+ { } as IntersectionObserver ,
427+ ) ;
428+ } ) ;
429+
430+ act ( ( ) => {
431+ jest . advanceTimersByTime ( 500 ) ;
432+ } ) ;
433+
434+ expect ( mockLogEvent ) . not . toHaveBeenCalled ( ) ;
435+ } ) ;
436+
437+ it ( "restarts the timer when the element re-enters after leaving" , ( ) => {
438+ const { result } = renderHook ( ( ) =>
439+ useImpressionRef ( "card.viewed" , { } , { minDurationMs : 250 } ) ,
440+ ) ;
441+
442+ const node = document . createElement ( "div" ) ;
443+ act ( ( ) => {
444+ result . current ( node ) ;
445+ } ) ;
446+
447+ act ( ( ) => {
448+ lastCallback (
449+ [ { isIntersecting : true } as IntersectionObserverEntry ] ,
450+ { } as IntersectionObserver ,
451+ ) ;
452+ } ) ;
453+ act ( ( ) => {
454+ jest . advanceTimersByTime ( 100 ) ;
455+ } ) ;
456+ act ( ( ) => {
457+ lastCallback (
458+ [ { isIntersecting : false } as IntersectionObserverEntry ] ,
459+ { } as IntersectionObserver ,
460+ ) ;
461+ } ) ;
462+
463+ // Re-enter — timer should restart from zero
464+ act ( ( ) => {
465+ lastCallback (
466+ [ { isIntersecting : true } as IntersectionObserverEntry ] ,
467+ { } as IntersectionObserver ,
468+ ) ;
469+ } ) ;
470+ act ( ( ) => {
471+ jest . advanceTimersByTime ( 249 ) ;
472+ } ) ;
473+ expect ( mockLogEvent ) . not . toHaveBeenCalled ( ) ;
474+
475+ act ( ( ) => {
476+ jest . advanceTimersByTime ( 1 ) ;
477+ } ) ;
478+ expect ( mockLogEvent ) . toHaveBeenCalledTimes ( 1 ) ;
479+ } ) ;
480+
481+ it ( "clears the pending timer on unmount" , ( ) => {
482+ const { result, unmount } = renderHook ( ( ) =>
483+ useImpressionRef ( "card.viewed" , { } , { minDurationMs : 250 } ) ,
484+ ) ;
485+
486+ const node = document . createElement ( "div" ) ;
487+ act ( ( ) => {
488+ result . current ( node ) ;
489+ } ) ;
490+ act ( ( ) => {
491+ lastCallback (
492+ [ { isIntersecting : true } as IntersectionObserverEntry ] ,
493+ { } as IntersectionObserver ,
494+ ) ;
495+ } ) ;
496+
497+ unmount ( ) ;
498+
499+ act ( ( ) => {
500+ jest . advanceTimersByTime ( 500 ) ;
501+ } ) ;
502+
503+ expect ( mockLogEvent ) . not . toHaveBeenCalled ( ) ;
504+ } ) ;
505+ } ) ;
358506} ) ;
0 commit comments