@@ -486,4 +486,194 @@ describe('Countdown Timer System', () => {
486486 ) ;
487487 } ) ;
488488 } ) ;
489+
490+ describe ( 'Visibility API Integration' , ( ) => {
491+ test ( 'stops timer when page becomes hidden' , ( ) => {
492+ const clearIntervalSpy = jest . spyOn ( global , 'clearInterval' ) ;
493+
494+ document . body . innerHTML = `
495+ <div class="countdown-display"
496+ data-deadline="2024-01-22 23:59:59">
497+ </div>
498+ ` ;
499+
500+ jest . isolateModules ( ( ) => {
501+ require ( '../../../static/js/countdown-simple.js' ) ;
502+ } ) ;
503+
504+ // Simulate page becoming hidden
505+ Object . defineProperty ( document , 'hidden' , {
506+ configurable : true ,
507+ writable : true ,
508+ value : true
509+ } ) ;
510+
511+ const event = new Event ( 'visibilitychange' ) ;
512+ document . dispatchEvent ( event ) ;
513+
514+ expect ( clearIntervalSpy ) . toHaveBeenCalled ( ) ;
515+ } ) ;
516+
517+ test ( 'restarts timer when page becomes visible' , ( ) => {
518+ const setIntervalSpy = jest . spyOn ( global , 'setInterval' ) ;
519+
520+ document . body . innerHTML = `
521+ <div class="countdown-display"
522+ data-deadline="2024-01-22 23:59:59">
523+ </div>
524+ ` ;
525+
526+ jest . isolateModules ( ( ) => {
527+ require ( '../../../static/js/countdown-simple.js' ) ;
528+ } ) ;
529+
530+ // Clear the spy count from initial load
531+ setIntervalSpy . mockClear ( ) ;
532+
533+ // First hide the page
534+ Object . defineProperty ( document , 'hidden' , {
535+ configurable : true ,
536+ writable : true ,
537+ value : true
538+ } ) ;
539+ document . dispatchEvent ( new Event ( 'visibilitychange' ) ) ;
540+
541+ // Then show it again
542+ Object . defineProperty ( document , 'hidden' , {
543+ configurable : true ,
544+ writable : true ,
545+ value : false
546+ } ) ;
547+ document . dispatchEvent ( new Event ( 'visibilitychange' ) ) ;
548+
549+ // Should restart the timer
550+ expect ( setIntervalSpy ) . toHaveBeenCalledWith ( expect . any ( Function ) , 1000 ) ;
551+ } ) ;
552+ } ) ;
553+
554+ describe ( 'CountdownManager Public API' , ( ) => {
555+ test ( 'refresh method updates all countdowns' , ( ) => {
556+ document . body . innerHTML = `
557+ <div class="countdown-display" id="test1"
558+ data-deadline="2024-01-22 23:59:59">
559+ </div>
560+ <div class="countdown-display" id="test2"
561+ data-deadline="2024-01-25 23:59:59">
562+ </div>
563+ ` ;
564+
565+ jest . isolateModules ( ( ) => {
566+ require ( '../../../static/js/countdown-simple.js' ) ;
567+ } ) ;
568+
569+ // Clear existing content
570+ document . getElementById ( 'test1' ) . textContent = '' ;
571+ document . getElementById ( 'test2' ) . textContent = '' ;
572+
573+ // Call refresh
574+ window . CountdownManager . refresh ( ) ;
575+
576+ // Both should be updated
577+ expect ( document . getElementById ( 'test1' ) . textContent ) . toBeTruthy ( ) ;
578+ expect ( document . getElementById ( 'test2' ) . textContent ) . toBeTruthy ( ) ;
579+ } ) ;
580+
581+ test ( 'destroy method clears timer' , ( ) => {
582+ const clearIntervalSpy = jest . spyOn ( global , 'clearInterval' ) ;
583+
584+ document . body . innerHTML = `
585+ <div class="countdown-display"
586+ data-deadline="2024-01-22 23:59:59">
587+ </div>
588+ ` ;
589+
590+ jest . isolateModules ( ( ) => {
591+ require ( '../../../static/js/countdown-simple.js' ) ;
592+ } ) ;
593+
594+ // Clear spy from initialization
595+ clearIntervalSpy . mockClear ( ) ;
596+
597+ // Destroy timers
598+ window . CountdownManager . destroy ( ) ;
599+
600+ expect ( clearIntervalSpy ) . toHaveBeenCalled ( ) ;
601+ } ) ;
602+
603+ test ( 'init clears existing timer before creating new one' , ( ) => {
604+ const clearIntervalSpy = jest . spyOn ( global , 'clearInterval' ) ;
605+ const setIntervalSpy = jest . spyOn ( global , 'setInterval' ) ;
606+
607+ document . body . innerHTML = `
608+ <div class="countdown-display"
609+ data-deadline="2024-01-22 23:59:59">
610+ </div>
611+ ` ;
612+
613+ jest . isolateModules ( ( ) => {
614+ require ( '../../../static/js/countdown-simple.js' ) ;
615+ } ) ;
616+
617+ // Clear spies
618+ clearIntervalSpy . mockClear ( ) ;
619+ setIntervalSpy . mockClear ( ) ;
620+
621+ // Call init again (should clear existing timer)
622+ window . CountdownManager . init ( ) ;
623+
624+ // Should clear and then set
625+ expect ( clearIntervalSpy ) . toHaveBeenCalled ( ) ;
626+ expect ( setIntervalSpy ) . toHaveBeenCalledWith ( expect . any ( Function ) , 1000 ) ;
627+ } ) ;
628+
629+ test ( 'onFilterUpdate method exists for compatibility' , ( ) => {
630+ jest . isolateModules ( ( ) => {
631+ require ( '../../../static/js/countdown-simple.js' ) ;
632+ } ) ;
633+
634+ expect ( window . CountdownManager . onFilterUpdate ) . toBeDefined ( ) ;
635+ expect ( typeof window . CountdownManager . onFilterUpdate ) . toBe ( 'function' ) ;
636+
637+ // Should not throw when called
638+ expect ( ( ) => {
639+ window . CountdownManager . onFilterUpdate ( ) ;
640+ } ) . not . toThrow ( ) ;
641+ } ) ;
642+ } ) ;
643+
644+ describe ( 'Document Ready State' , ( ) => {
645+ test ( 'waits for DOMContentLoaded when document is loading' , ( ) => {
646+ // Save original readyState
647+ const originalReadyState = document . readyState ;
648+
649+ // Mock document.readyState
650+ Object . defineProperty ( document , 'readyState' , {
651+ configurable : true ,
652+ writable : true ,
653+ value : 'loading'
654+ } ) ;
655+
656+ const addEventListenerSpy = jest . spyOn ( document , 'addEventListener' ) ;
657+
658+ document . body . innerHTML = `
659+ <div class="countdown-display"
660+ data-deadline="2024-01-22 23:59:59">
661+ </div>
662+ ` ;
663+
664+ jest . isolateModules ( ( ) => {
665+ require ( '../../../static/js/countdown-simple.js' ) ;
666+ } ) ;
667+
668+ // Should have added DOMContentLoaded listener
669+ expect ( addEventListenerSpy ) . toHaveBeenCalledWith ( 'DOMContentLoaded' , expect . any ( Function ) ) ;
670+
671+ // Restore original readyState
672+ Object . defineProperty ( document , 'readyState' , {
673+ configurable : true ,
674+ writable : true ,
675+ value : originalReadyState
676+ } ) ;
677+ } ) ;
678+ } ) ;
489679} ) ;
0 commit comments