@@ -422,6 +422,8 @@ describe('FileList', () => {
422
422
} )
423
423
424
424
describe ( 'addFile' , ( ) => {
425
+ var clock = null
426
+
425
427
beforeEach ( ( ) => {
426
428
patternList = PATTERN_LIST
427
429
mg = MG
@@ -435,14 +437,25 @@ describe('FileList', () => {
435
437
statCache : mg . statCache
436
438
} )
437
439
}
440
+
441
+ clock = sinon . useFakeTimers ( )
442
+ // This hack is needed to ensure lodash is using the fake timers
443
+ // from sinon
438
444
List = proxyquire ( '../../lib/file-list' , {
445
+ lodash : _ . runInContext ( ) ,
439
446
helper : helper ,
440
447
glob : glob ,
448
+ 'graceful-fs' : mockFs ,
441
449
path : pathLib . posix || pathLib /* for node 0.10 */ ,
442
- 'graceful-fs' : mockFs
450
+ bluebird : Promise
443
451
} )
444
452
445
- list = new List ( patterns ( '/some/*.js' , '*.txt' ) , [ '/secret/*.txt' ] , emitter , preprocess )
453
+ list = new List ( patterns ( '/some/*.js' , '*.txt' ) , [ '/secret/*.txt' ] , emitter , preprocess , 100 )
454
+ } )
455
+
456
+ afterEach ( ( ) => {
457
+ clock . restore ( )
458
+ Promise . setScheduler ( ( fn ) => process . nextTick ( fn ) )
446
459
} )
447
460
448
461
it ( 'does not add excluded files' , ( ) => {
@@ -487,6 +500,7 @@ describe('FileList', () => {
487
500
modified . reset ( )
488
501
489
502
return list . addFile ( '/some/d.js' ) . then ( ( ) => {
503
+ clock . tick ( 101 )
490
504
expect ( modified ) . to . have . been . calledOnce
491
505
} )
492
506
} )
@@ -534,9 +548,12 @@ describe('FileList', () => {
534
548
} )
535
549
536
550
describe ( 'changeFile' , ( ) => {
551
+ var clock = null
552
+
537
553
beforeEach ( ( ) => {
538
554
patternList = PATTERN_LIST
539
555
mg = MG
556
+ Promise . setScheduler ( ( fn ) => fn ( ) )
540
557
541
558
preprocess = sinon . spy ( ( file , done ) => process . nextTick ( done ) )
542
559
emitter = new EventEmitter ( )
@@ -548,20 +565,30 @@ describe('FileList', () => {
548
565
} )
549
566
}
550
567
568
+ clock = sinon . useFakeTimers ( )
569
+ // This hack is needed to ensure lodash is using the fake timers
570
+ // from sinon
551
571
List = proxyquire ( '../../lib/file-list' , {
572
+ lodash : _ . runInContext ( ) ,
552
573
helper : helper ,
553
574
glob : glob ,
575
+ 'graceful-fs' : mockFs ,
554
576
path : pathLib . posix || pathLib /* for node 0.10 */ ,
555
- 'graceful-fs' : mockFs
577
+ bluebird : Promise
556
578
} )
557
579
558
580
mockFs . _touchFile ( '/some/a.js' , '2012-04-04' )
559
581
mockFs . _touchFile ( '/some/b.js' , '2012-05-05' )
560
582
} )
561
583
584
+ afterEach ( ( ) => {
585
+ clock . restore ( )
586
+ Promise . setScheduler ( ( fn ) => process . nextTick ( fn ) )
587
+ } )
588
+
562
589
it ( 'updates mtime and fires "file_list_modified"' , ( ) => {
563
590
// MATCH: /some/a.js, /some/b.js
564
- list = new List ( patterns ( '/some/*.js' , '/a.*' ) , [ ] , emitter , preprocess )
591
+ list = new List ( patterns ( '/some/*.js' , '/a.*' ) , [ ] , emitter , preprocess , 100 )
565
592
var modified = sinon . stub ( )
566
593
emitter . on ( 'file_list_modified' , modified )
567
594
@@ -570,6 +597,7 @@ describe('FileList', () => {
570
597
modified . reset ( )
571
598
572
599
return list . changeFile ( '/some/b.js' ) . then ( ( files ) => {
600
+ clock . tick ( 101 )
573
601
expect ( modified ) . to . have . been . calledOnce
574
602
expect ( findFile ( '/some/b.js' , files . served ) . mtime ) . to . be . eql ( new Date ( '2020-01-01' ) )
575
603
} )
@@ -627,9 +655,12 @@ describe('FileList', () => {
627
655
} )
628
656
629
657
describe ( 'removeFile' , ( ) => {
658
+ var clock = null
659
+
630
660
beforeEach ( ( ) => {
631
661
patternList = PATTERN_LIST
632
662
mg = MG
663
+ Promise . setScheduler ( ( fn ) => fn ( ) )
633
664
634
665
preprocess = sinon . spy ( ( file , done ) => process . nextTick ( done ) )
635
666
emitter = new EventEmitter ( )
@@ -641,20 +672,30 @@ describe('FileList', () => {
641
672
} )
642
673
}
643
674
675
+ clock = sinon . useFakeTimers ( )
676
+ // This hack is needed to ensure lodash is using the fake timers
677
+ // from sinon
644
678
List = proxyquire ( '../../lib/file-list' , {
679
+ lodash : _ . runInContext ( ) ,
645
680
helper : helper ,
646
681
glob : glob ,
682
+ 'graceful-fs' : mockFs ,
647
683
path : pathLib . posix || pathLib /* for node 0.10 */ ,
648
- 'graceful-fs' : mockFs
684
+ bluebird : Promise
649
685
} )
650
686
651
687
modified = sinon . stub ( )
652
688
emitter . on ( 'file_list_modified' , modified )
653
689
} )
654
690
691
+ afterEach ( ( ) => {
692
+ clock . restore ( )
693
+ Promise . setScheduler ( ( fn ) => process . nextTick ( fn ) )
694
+ } )
695
+
655
696
it ( 'removes the file from the list and fires "file_list_modified"' , ( ) => {
656
697
// MATCH: /some/a.js, /some/b.js, /a.txt
657
- list = new List ( patterns ( '/some/*.js' , '/a.*' ) , [ ] , emitter , preprocess )
698
+ list = new List ( patterns ( '/some/*.js' , '/a.*' ) , [ ] , emitter , preprocess , 100 )
658
699
659
700
var modified = sinon . stub ( )
660
701
emitter . on ( 'file_list_modified' , modified )
@@ -667,6 +708,7 @@ describe('FileList', () => {
667
708
'/some/b.js' ,
668
709
'/a.txt'
669
710
] )
711
+ clock . tick ( 101 )
670
712
expect ( modified ) . to . have . been . calledOnce
671
713
} )
672
714
} )
@@ -685,6 +727,15 @@ describe('FileList', () => {
685
727
} )
686
728
687
729
describe ( 'batch interval' , ( ) => {
730
+ // IMPORTANT: When writing tests for debouncing behaviour, you must wait for the promise
731
+ // returned by list.changeFile or list.addFile. list.removeFile calls self._emitModified()
732
+ // in a different manner and doesn't *need* to be waited on. If you use this behaviour
733
+ // in your tests it can can lead to very confusing results when they are modified or
734
+ // extended.
735
+ //
736
+ // Rule of thumb: Always wait on the promises returned by list.addFile, list.changeFile,
737
+ // and list.removeFile.
738
+
688
739
var clock = null
689
740
690
741
beforeEach ( ( ) => {
@@ -723,7 +774,97 @@ describe('FileList', () => {
723
774
Promise . setScheduler ( ( fn ) => process . nextTick ( fn ) )
724
775
} )
725
776
726
- it ( 'batches multiple changes within an interval' , ( ) => {
777
+ it ( 'debounces calls to emitModified' , ( ) => {
778
+ list = new List ( patterns ( ) , [ ] , emitter , preprocess , 100 )
779
+
780
+ return list . refresh ( ) . then ( ( ) => {
781
+ modified . reset ( )
782
+ list . _emitModified ( )
783
+ clock . tick ( 99 )
784
+ expect ( modified ) . to . not . have . been . called
785
+ list . _emitModified ( )
786
+ clock . tick ( 2 )
787
+ expect ( modified ) . to . not . have . been . called
788
+ clock . tick ( 97 )
789
+ expect ( modified ) . to . not . have . been . called
790
+ clock . tick ( 2 )
791
+ expect ( modified ) . to . have . been . calledOnce
792
+ clock . tick ( 1000 )
793
+ expect ( modified ) . to . have . been . calledOnce
794
+ list . _emitModified ( )
795
+ clock . tick ( 99 )
796
+ expect ( modified ) . to . have . been . calledOnce
797
+ clock . tick ( 2 )
798
+ expect ( modified ) . to . have . been . calledTwice
799
+ } )
800
+ } )
801
+
802
+ it ( 'debounces a single file change' , ( ) => {
803
+ list = new List ( patterns ( '/some/*.js' , '/a.*' ) , [ ] , emitter , preprocess , 100 )
804
+
805
+ return list . refresh ( ) . then ( ( files ) => {
806
+ modified . reset ( )
807
+ // Even with no changes, all these files are served
808
+ list . addFile ( '/some/0.js' ) . then ( ( ) => {
809
+ clock . tick ( 99 )
810
+ expect ( modified ) . to . not . have . been . called
811
+
812
+ clock . tick ( 2 )
813
+ expect ( modified ) . to . have . been . calledOnce
814
+
815
+ files = modified . lastCall . args [ 0 ]
816
+ expect ( pathsFrom ( files . served ) ) . to . be . eql ( [
817
+ '/some/0.js' ,
818
+ '/some/a.js' ,
819
+ '/some/b.js' ,
820
+ '/a.txt'
821
+ ] )
822
+ } )
823
+ } )
824
+ } )
825
+
826
+ it ( 'debounces several changes to a file' , ( ) => {
827
+ list = new List ( patterns ( '/some/*.js' , '/a.*' ) , [ ] , emitter , preprocess , 100 )
828
+
829
+ return list . refresh ( ) . then ( ( files ) => {
830
+ modified . reset ( )
831
+ list . addFile ( '/some/0.js' ) . then ( ( ) => {
832
+ clock . tick ( 99 )
833
+ expect ( modified ) . to . not . have . been . called
834
+
835
+ // Modify file, must change mtime too, or change is ignored
836
+ mockFs . _touchFile ( '/some/0.js' , '2020-01-01' )
837
+ list . changeFile ( '/some/0.js' ) . then ( ( ) => {
838
+ // Ensure that the debounce timer was reset
839
+ clock . tick ( 2 )
840
+ expect ( modified ) . to . not . have . been . called
841
+
842
+ // Ensure that debounce timer fires after 100ms
843
+ clock . tick ( 99 )
844
+ expect ( modified ) . to . have . been . calledOnce
845
+
846
+ // Make sure there aren't any lingering debounce calls
847
+ clock . tick ( 1000 )
848
+
849
+ // Modify file (one hour later mtime)
850
+ expect ( modified ) . to . have . been . calledOnce
851
+ mockFs . _touchFile ( '/some/0.js' , '2020-01-02' )
852
+ list . changeFile ( '/some/0.js' ) . then ( ( ) => {
853
+ clock . tick ( 99 )
854
+ expect ( modified ) . to . have . been . calledOnce
855
+ clock . tick ( 2 )
856
+ expect ( modified ) . to . have . been . calledTwice
857
+
858
+ // Make sure there aren't any lingering calls
859
+ clock . tick ( 1000 )
860
+ expect ( modified ) . to . have . been . calledTwice
861
+ } )
862
+ } )
863
+ } )
864
+ } )
865
+ } )
866
+
867
+ it ( 'debounces multiple changes until there is quiescence' , ( ) => {
727
868
// MATCH: /some/a.js, /some/b.js, /a.txt
728
869
list = new List ( patterns ( '/some/*.js' , '/a.*' ) , [ ] , emitter , preprocess , 100 )
729
870
@@ -734,20 +875,28 @@ describe('FileList', () => {
734
875
list . removeFile ( '/some/a.js' ) // /some/b.js, /a.txt
735
876
list . removeFile ( '/a.txt' ) // /some/b.js
736
877
list . addFile ( '/a.txt' ) // /some/b.js, /a.txt
737
- list . addFile ( '/some/0.js' ) // /some/0.js, /some/b.js, /a.txt
738
-
739
- clock . tick ( 99 )
740
- expect ( modified ) . to . not . have . been . called
741
-
742
- clock . tick ( 2 )
743
- expect ( modified ) . to . have . been . calledOnce
744
-
745
- files = modified . lastCall . args [ 0 ]
746
- expect ( pathsFrom ( files . served ) ) . to . be . eql ( [
747
- '/some/0.js' ,
748
- '/some/b.js' ,
749
- '/a.txt'
750
- ] )
878
+ list . addFile ( '/some/0.js' ) . then ( ( ) => { // /some/0.js, /some/b.js, /a.txt
879
+ clock . tick ( 99 )
880
+ expect ( modified ) . to . not . have . been . called
881
+ mockFs . _touchFile ( '/a.txt' , '2020-01-01' )
882
+ list . changeFile ( '/a.txt' ) . then ( ( ) => {
883
+ clock . tick ( 2 )
884
+ expect ( modified ) . to . not . have . been . called
885
+
886
+ clock . tick ( 100 )
887
+ expect ( modified ) . to . have . been . calledOnce
888
+
889
+ clock . tick ( 1000 )
890
+ expect ( modified ) . to . have . been . calledOnce
891
+
892
+ files = modified . lastCall . args [ 0 ]
893
+ expect ( pathsFrom ( files . served ) ) . to . be . eql ( [
894
+ '/some/0.js' ,
895
+ '/some/b.js' ,
896
+ '/a.txt'
897
+ ] )
898
+ } )
899
+ } )
751
900
} )
752
901
} )
753
902
0 commit comments