@@ -32,17 +32,6 @@ afterAll(() => {
3232 } ) ;
3333} ) ;
3434
35- // jsdom doesn't implement ResizeObserver — provide a minimal no-op stub so any
36- // test that calls setupAutocapture without a specialised mock still works.
37- // The scroll depth describe block overrides this with a controllable mock.
38- ( global as Record < string , unknown > ) . ResizeObserver = class {
39- // eslint-disable-next-line class-methods-use-this
40- observe ( ) { /* no-op */ }
41-
42- // eslint-disable-next-line class-methods-use-this
43- disconnect ( ) { /* no-op */ }
44- } ;
45-
4635describe ( 'autocapture' , ( ) => {
4736 let enqueue : jest . Mock ;
4837 let consent : ConsentLevel ;
@@ -562,19 +551,11 @@ describe('autocapture', () => {
562551 let rafCallbacks : Array < ( ) => void > ;
563552 let originalRAF : typeof requestAnimationFrame ;
564553 let originalCAF : typeof cancelAnimationFrame ;
565- let resizeCallback : ( ( ) => void ) | null ;
566- let originalResizeObserver : typeof ResizeObserver ;
567-
568- function fireResizeObserver ( ) {
569- resizeCallback ?.( ) ;
570- }
571554
572555 beforeEach ( ( ) => {
573556 rafCallbacks = [ ] ;
574557 originalRAF = window . requestAnimationFrame ;
575558 originalCAF = window . cancelAnimationFrame ;
576- resizeCallback = null ;
577- originalResizeObserver = ( global as Record < string , unknown > ) . ResizeObserver as typeof ResizeObserver ;
578559
579560 // Mock rAF: collect callbacks, flush manually
580561 let nextId = 1 ;
@@ -584,26 +565,11 @@ describe('autocapture', () => {
584565 return id ;
585566 } ) ;
586567 window . cancelAnimationFrame = jest . fn ( ) ;
587-
588- // Mock ResizeObserver. Real RO delivers entries on a microtask/rAF; this
589- // mock fires synchronously inside observe() and via fireResizeObserver()
590- // for subsequent triggers. Outcome is equivalent for all current cases,
591- // but tests won't catch ordering bugs that depend on the async boundary.
592- ( global as Record < string , unknown > ) . ResizeObserver = class MockResizeObserver {
593- constructor ( cb : ( ) => void ) { resizeCallback = cb ; }
594-
595- // eslint-disable-next-line class-methods-use-this
596- observe ( ) { resizeCallback ?.( ) ; }
597-
598- // eslint-disable-next-line class-methods-use-this
599- disconnect ( ) { /* no-op */ }
600- } ;
601568 } ) ;
602569
603570 afterEach ( ( ) => {
604571 window . requestAnimationFrame = originalRAF ;
605572 window . cancelAnimationFrame = originalCAF ;
606- ( global as Record < string , unknown > ) . ResizeObserver = originalResizeObserver ;
607573 } ) ;
608574
609575 function flushRAF ( ) {
@@ -714,17 +680,6 @@ describe('autocapture', () => {
714680 expect ( enqueue ) . toHaveBeenCalledTimes ( 5 ) ;
715681 } ) ;
716682
717- it ( 'does not include aboveFold property on scrollable pages' , ( ) => {
718- setup ( { scroll : true } ) ;
719-
720- ( window as Record < string , unknown > ) . scrollY = 375 ;
721- window . dispatchEvent ( new Event ( 'scroll' ) ) ;
722- flushRAF ( ) ;
723-
724- expect ( enqueue . mock . calls [ 0 ] [ 1 ] ) . toEqual ( { depth : 25 } ) ;
725- expect ( enqueue . mock . calls [ 0 ] [ 1 ] ) . not . toHaveProperty ( 'aboveFold' ) ;
726- } ) ;
727-
728683 it ( 'does not fire at consent none' , ( ) => {
729684 consent = 'none' ;
730685 setup ( { scroll : true } ) ;
@@ -763,88 +718,29 @@ describe('autocapture', () => {
763718 } ) ;
764719 } ) ;
765720
766- describe ( 'above-the-fold pages' , ( ) => {
721+ describe ( 'non-scrollable pages' , ( ) => {
767722 beforeEach ( ( ) => {
768- // 400px content in a 600px viewport → no scroll
723+ // 400px content in a 600px viewport → document does not scroll.
724+ // Same shape applies to SPAs / pages with internal scroll containers
725+ // where document.documentElement.scrollHeight equals window.innerHeight.
769726 setScrollGeometry ( 400 , 600 , 0 ) ;
770- jest . useFakeTimers ( ) ;
771- } ) ;
772-
773- afterEach ( ( ) => {
774- jest . useRealTimers ( ) ;
775- } ) ;
776-
777- it ( 'fires scroll_depth 100 with aboveFold after dwell time' , ( ) => {
778- setup ( { scroll : true } ) ;
779-
780- // Should NOT fire immediately
781- expect ( enqueue ) . not . toHaveBeenCalled ( ) ;
782-
783- // Advance past dwell time
784- jest . advanceTimersByTime ( 2000 ) ;
785-
786- expect ( enqueue ) . toHaveBeenCalledWith ( 'scroll_depth' , {
787- depth : 100 ,
788- aboveFold : true ,
789- } ) ;
790- expect ( enqueue ) . toHaveBeenCalledTimes ( 1 ) ;
791- } ) ;
792-
793- it ( 'does not fire before dwell time elapses' , ( ) => {
794- setup ( { scroll : true } ) ;
795-
796- jest . advanceTimersByTime ( 1999 ) ;
797- expect ( enqueue ) . not . toHaveBeenCalled ( ) ;
798727 } ) ;
799728
800- it ( 'does not fire if consent is none when dwell timer triggers ' , ( ) => {
729+ it ( 'does not fire any milestones on setup ' , ( ) => {
801730 setup ( { scroll : true } ) ;
802-
803- consent = 'none' ;
804- jest . advanceTimersByTime ( 2000 ) ;
805-
806731 expect ( enqueue ) . not . toHaveBeenCalled ( ) ;
807732 } ) ;
808733
809- it ( 'cancels dwell timer on teardown ' , ( ) => {
734+ it ( 'does not fire any milestones on subsequent scroll events ' , ( ) => {
810735 setup ( { scroll : true } ) ;
811736
812- teardown ( ) ;
813- jest . advanceTimersByTime ( 2000 ) ;
814-
815- expect ( enqueue ) . not . toHaveBeenCalled ( ) ;
816- } ) ;
817-
818- it ( 'cancels dwell timer when page grows beyond viewport' , ( ) => {
819- setup ( { scroll : true } ) ;
820-
821- // Sanity check: the dwell timer was actually scheduled (otherwise the
822- // assertion below passes vacuously). Advance partway, no fire yet.
823- jest . advanceTimersByTime ( 1000 ) ;
824- expect ( enqueue ) . not . toHaveBeenCalled ( ) ;
825-
826- // Simulate content loading and page growing.
827- setScrollGeometry ( 2000 , 600 , 0 ) ;
828- fireResizeObserver ( ) ;
737+ // Even if a scroll event fires (e.g. iOS overscroll bounce), there is
738+ // nothing to scroll past, so no milestone should fire.
739+ window . dispatchEvent ( new Event ( 'scroll' ) ) ;
740+ flushRAF ( ) ;
829741
830- // Advance well past dwell time — no above-fold event should fire.
831- jest . advanceTimersByTime ( 5000 ) ;
832742 expect ( enqueue ) . not . toHaveBeenCalled ( ) ;
833743 } ) ;
834-
835- it ( 'does not throw or attach observer when ResizeObserver is unavailable' , ( ) => {
836- const original = ( global as Record < string , unknown > ) . ResizeObserver ;
837- delete ( global as Record < string , unknown > ) . ResizeObserver ;
838-
839- try {
840- expect ( ( ) => setup ( { scroll : true } ) ) . not . toThrow ( ) ;
841- // No above-fold event ever fires (no observer to start the timer).
842- jest . advanceTimersByTime ( 2000 ) ;
843- expect ( enqueue ) . not . toHaveBeenCalled ( ) ;
844- } finally {
845- ( global as Record < string , unknown > ) . ResizeObserver = original ;
846- }
847- } ) ;
848744 } ) ;
849745
850746 describe ( 'configuration' , ( ) => {
0 commit comments