1
- import {
2
- async ,
3
- ComponentFixture ,
4
- fakeAsync ,
5
- flushMicrotasks ,
6
- TestBed ,
7
- tick ,
8
- } from '@angular/core/testing' ;
1
+ import { ComponentFixture , fakeAsync , TestBed , tick , flush } from '@angular/core/testing' ;
9
2
import { FormControl , FormsModule , NgModel , ReactiveFormsModule } from '@angular/forms' ;
10
3
import { Component , DebugElement } from '@angular/core' ;
11
4
import { By } from '@angular/platform-browser' ;
12
5
import { dispatchFakeEvent } from '@angular/cdk/testing' ;
13
6
import { MatCheckbox , MatCheckboxChange , MatCheckboxModule } from './index' ;
14
7
import { RIPPLE_FADE_IN_DURATION , RIPPLE_FADE_OUT_DURATION } from '@angular/material/core' ;
15
8
import { MAT_CHECKBOX_CLICK_ACTION } from './checkbox-config' ;
9
+ import { MutationObserverFactory } from '@angular/cdk/observers' ;
16
10
17
11
18
12
describe ( 'MatCheckbox' , ( ) => {
19
13
let fixture : ComponentFixture < any > ;
20
14
21
- beforeEach ( async ( ( ) => {
15
+ beforeEach ( fakeAsync ( ( ) => {
22
16
TestBed . configureTestingModule ( {
23
17
imports : [ MatCheckboxModule , FormsModule , ReactiveFormsModule ] ,
24
18
declarations : [
@@ -116,7 +110,7 @@ describe('MatCheckbox', () => {
116
110
fixture . detectChanges ( ) ;
117
111
118
112
// Flush the microtasks because the forms module updates the model state asynchronously.
119
- flushMicrotasks ( ) ;
113
+ flush ( ) ;
120
114
121
115
// The checked property has been updated from the model and now the view needs
122
116
// to reflect the state change.
@@ -141,7 +135,7 @@ describe('MatCheckbox', () => {
141
135
fixture . detectChanges ( ) ;
142
136
143
137
// Flush the microtasks because the forms module updates the model state asynchronously.
144
- flushMicrotasks ( ) ;
138
+ flush ( ) ;
145
139
146
140
// The checked property has been updated from the model and now the view needs
147
141
// to reflect the state change.
@@ -153,7 +147,7 @@ describe('MatCheckbox', () => {
153
147
expect ( testComponent . isIndeterminate ) . toBe ( false ) ;
154
148
} ) ) ;
155
149
156
- it ( 'should not set indeterminate to false when checked is set programmatically' , async ( ( ) => {
150
+ it ( 'should not set indeterminate to false when checked is set programmatically' , ( ) => {
157
151
testComponent . isIndeterminate = true ;
158
152
fixture . detectChanges ( ) ;
159
153
@@ -176,7 +170,7 @@ describe('MatCheckbox', () => {
176
170
expect ( inputElement . indeterminate ) . toBe ( true ) ;
177
171
expect ( inputElement . checked ) . toBe ( false ) ;
178
172
expect ( testComponent . isIndeterminate ) . toBe ( true ) ;
179
- } ) ) ;
173
+ } ) ;
180
174
181
175
it ( 'should change native element checked when check programmatically' , ( ) => {
182
176
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -212,7 +206,7 @@ describe('MatCheckbox', () => {
212
206
checkboxInstance . _onInputClick ( < Event > { stopPropagation : ( ) => { } } ) ;
213
207
214
208
// Flush the microtasks because the indeterminate state will be updated in the next tick.
215
- flushMicrotasks ( ) ;
209
+ flush ( ) ;
216
210
217
211
expect ( checkboxInstance . checked ) . toBe ( true ) ;
218
212
expect ( checkboxInstance . indeterminate ) . toBe ( false ) ;
@@ -262,7 +256,7 @@ describe('MatCheckbox', () => {
262
256
fixture . detectChanges ( ) ;
263
257
264
258
// Flush the microtasks because the indeterminate state will be updated in the next tick.
265
- flushMicrotasks ( ) ;
259
+ flush ( ) ;
266
260
267
261
expect ( checkboxInstance . checked ) . toBe ( true ) ;
268
262
expect ( checkboxInstance . indeterminate ) . toBe ( false ) ;
@@ -317,7 +311,7 @@ describe('MatCheckbox', () => {
317
311
expect ( testComponent . onCheckboxClick ) . toHaveBeenCalledTimes ( 1 ) ;
318
312
} ) ;
319
313
320
- it ( 'should trigger a change event when the native input does' , async ( ( ) => {
314
+ it ( 'should trigger a change event when the native input does' , fakeAsync ( ( ) => {
321
315
spyOn ( testComponent , 'onCheckboxChange' ) ;
322
316
323
317
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -329,16 +323,15 @@ describe('MatCheckbox', () => {
329
323
expect ( inputElement . checked ) . toBe ( true ) ;
330
324
expect ( checkboxNativeElement . classList ) . toContain ( 'mat-checkbox-checked' ) ;
331
325
332
- // Wait for the fixture to become stable, because the EventEmitter for the change event,
333
- // will only fire after the zone async change detection has finished.
334
- fixture . whenStable ( ) . then ( ( ) => {
335
- // The change event shouldn't fire, because the value change was not caused
336
- // by any interaction.
337
- expect ( testComponent . onCheckboxChange ) . toHaveBeenCalledTimes ( 1 ) ;
338
- } ) ;
326
+ fixture . detectChanges ( ) ;
327
+ flush ( ) ;
328
+
329
+ // The change event shouldn't fire, because the value change was not caused
330
+ // by any interaction.
331
+ expect ( testComponent . onCheckboxChange ) . toHaveBeenCalledTimes ( 1 ) ;
339
332
} ) ) ;
340
333
341
- it ( 'should not trigger the change event by changing the native value' , async ( ( ) => {
334
+ it ( 'should not trigger the change event by changing the native value' , fakeAsync ( ( ) => {
342
335
spyOn ( testComponent , 'onCheckboxChange' ) ;
343
336
344
337
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -350,14 +343,12 @@ describe('MatCheckbox', () => {
350
343
expect ( inputElement . checked ) . toBe ( true ) ;
351
344
expect ( checkboxNativeElement . classList ) . toContain ( 'mat-checkbox-checked' ) ;
352
345
353
- // Wait for the fixture to become stable, because the EventEmitter for the change event,
354
- // will only fire after the zone async change detection has finished.
355
- fixture . whenStable ( ) . then ( ( ) => {
356
- // The change event shouldn't fire, because the value change was not caused
357
- // by any interaction.
358
- expect ( testComponent . onCheckboxChange ) . not . toHaveBeenCalled ( ) ;
359
- } ) ;
346
+ fixture . detectChanges ( ) ;
347
+ flush ( ) ;
360
348
349
+ // The change event shouldn't fire, because the value change was not caused
350
+ // by any interaction.
351
+ expect ( testComponent . onCheckboxChange ) . not . toHaveBeenCalled ( ) ;
361
352
} ) ) ;
362
353
363
354
it ( 'should forward the required attribute' , ( ) => {
@@ -576,7 +567,7 @@ describe('MatCheckbox', () => {
576
567
inputElement . click ( ) ;
577
568
578
569
fixture . detectChanges ( ) ;
579
- flushMicrotasks ( ) ;
570
+ flush ( ) ;
580
571
fixture . detectChanges ( ) ;
581
572
expect ( inputElement . checked ) . toBe ( true ) ;
582
573
expect ( checkboxNativeElement . classList ) . toContain ( 'mat-checkbox-checked' ) ;
@@ -614,7 +605,7 @@ describe('MatCheckbox', () => {
614
605
inputElement . click ( ) ;
615
606
616
607
fixture . detectChanges ( ) ;
617
- flushMicrotasks ( ) ;
608
+ flush ( ) ;
618
609
fixture . detectChanges ( ) ;
619
610
620
611
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -630,7 +621,7 @@ describe('MatCheckbox', () => {
630
621
inputElement . click ( ) ;
631
622
632
623
fixture . detectChanges ( ) ;
633
- flushMicrotasks ( ) ;
624
+ flush ( ) ;
634
625
fixture . detectChanges ( ) ;
635
626
636
627
expect ( inputElement . checked ) . toBe ( true ) ;
@@ -642,7 +633,7 @@ describe('MatCheckbox', () => {
642
633
inputElement . click ( ) ;
643
634
644
635
fixture . detectChanges ( ) ;
645
- flushMicrotasks ( ) ;
636
+ flush ( ) ;
646
637
fixture . detectChanges ( ) ;
647
638
648
639
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -689,22 +680,20 @@ describe('MatCheckbox', () => {
689
680
expect ( changeSpy ) . toHaveBeenCalledTimes ( 1 ) ;
690
681
} ) ;
691
682
692
- it ( 'should not emit a DOM event to the change output' , async ( ( ) => {
683
+ it ( 'should not emit a DOM event to the change output' , fakeAsync ( ( ) => {
693
684
fixture . detectChanges ( ) ;
694
685
expect ( testComponent . lastEvent ) . toBeUndefined ( ) ;
695
686
696
687
// Trigger the click on the inputElement, because the input will probably
697
688
// emit a DOM event to the change output.
698
689
inputElement . click ( ) ;
699
690
fixture . detectChanges ( ) ;
691
+ flush ( ) ;
700
692
701
- fixture . whenStable ( ) . then ( ( ) => {
702
- // We're checking the arguments type / emitted value to be a boolean, because sometimes the
703
- // emitted value can be a DOM Event, which is not valid.
704
- // See angular/angular#4059
705
- expect ( testComponent . lastEvent . checked ) . toBe ( true ) ;
706
- } ) ;
707
-
693
+ // We're checking the arguments type / emitted value to be a boolean, because sometimes the
694
+ // emitted value can be a DOM Event, which is not valid.
695
+ // See angular/angular#4059
696
+ expect ( testComponent . lastEvent . checked ) . toBe ( true ) ;
708
697
} ) ) ;
709
698
} ) ;
710
699
@@ -789,7 +778,7 @@ describe('MatCheckbox', () => {
789
778
790
779
describe ( 'with native tabindex attribute' , ( ) => {
791
780
792
- it ( 'should properly detect native tabindex attribute' , async ( ( ) => {
781
+ it ( 'should properly detect native tabindex attribute' , fakeAsync ( ( ) => {
793
782
fixture = TestBed . createComponent ( CheckboxWithTabindexAttr ) ;
794
783
fixture . detectChanges ( ) ;
795
784
@@ -835,7 +824,7 @@ describe('MatCheckbox', () => {
835
824
} ) ;
836
825
837
826
it ( 'should be in pristine, untouched, and valid states initially' , fakeAsync ( ( ) => {
838
- flushMicrotasks ( ) ;
827
+ flush ( ) ;
839
828
840
829
let checkboxElement = fixture . debugElement . query ( By . directive ( MatCheckbox ) ) ;
841
830
let ngModel = checkboxElement . injector . get < NgModel > ( NgModel ) ;
@@ -968,35 +957,63 @@ describe('MatCheckbox', () => {
968
957
. toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
969
958
} ) ;
970
959
971
- it ( 'should not remove margin if initial label is set through binding' , async ( ( ) => {
960
+ it ( 'should not remove margin if initial label is set through binding' , ( ) => {
972
961
testComponent . label = 'Some content' ;
973
962
fixture . detectChanges ( ) ;
974
963
975
964
expect ( checkboxInnerContainer . classList )
976
965
. not . toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
977
- } ) ) ;
966
+ } ) ;
967
+
968
+ it ( 'should re-add margin if label is added asynchronously' , ( ) => {
969
+ fixture . destroy ( ) ;
970
+
971
+ const mutationCallbacks : Function [ ] = [ ] ;
972
+
973
+ TestBed
974
+ . resetTestingModule ( )
975
+ . configureTestingModule ( {
976
+ imports : [ MatCheckboxModule , FormsModule , ReactiveFormsModule ] ,
977
+ declarations : [ CheckboxWithoutLabel ] ,
978
+ providers : [ {
979
+ provide : MutationObserverFactory ,
980
+ useValue : {
981
+ // Stub out the factory that creates mutation observers for the underlying directive
982
+ // to allows us to flush out the callbacks asynchronously.
983
+ create : ( callback : Function ) => {
984
+ mutationCallbacks . push ( callback ) ;
985
+
986
+ return {
987
+ observe : ( ) => { } ,
988
+ disconnect : ( ) => { }
989
+ } ;
990
+ }
991
+ }
992
+ } ]
993
+ } )
994
+ . compileComponents ( ) ;
995
+
996
+ fixture = TestBed . createComponent ( CheckboxWithoutLabel ) ;
997
+ checkboxInnerContainer = fixture . debugElement
998
+ . query ( By . css ( '.mat-checkbox-inner-container' ) ) . nativeElement ;
978
999
979
- it ( 'should re-add margin if label is added asynchronously' , async ( ( ) => {
980
1000
fixture . detectChanges ( ) ;
981
1001
982
1002
expect ( checkboxInnerContainer . classList )
983
1003
. toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
984
1004
985
- testComponent . label = 'Some content' ;
1005
+ fixture . componentInstance . label = 'Some content' ;
986
1006
fixture . detectChanges ( ) ;
1007
+ mutationCallbacks . forEach ( callback => callback ( ) ) ;
987
1008
988
- // Wait for the MutationObserver to detect the content change and for the cdkObserveContent
989
- // to emit the change event to the checkbox.
990
- setTimeout ( ( ) => {
991
- // The MutationObserver from the cdkObserveContent directive detected the content change
992
- // and notified the checkbox component. The checkbox then marks the component as dirty
993
- // by calling `markForCheck()`. This needs to be reflected by the component template then.
994
- fixture . detectChanges ( ) ;
1009
+ // The MutationObserver from the cdkObserveContent directive detected the content change
1010
+ // and notified the checkbox component. The checkbox then marks the component as dirty
1011
+ // by calling `markForCheck()`. This needs to be reflected by the component template then.
1012
+ fixture . detectChanges ( ) ;
995
1013
996
- expect ( checkboxInnerContainer . classList )
997
- . not . toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
998
- } , 1 ) ;
999
- } ) ) ;
1014
+ expect ( checkboxInnerContainer . classList )
1015
+ . not . toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
1016
+ } ) ;
1000
1017
1001
1018
it ( 'should not add the "name" attribute if it is not passed in' , ( ) => {
1002
1019
fixture . detectChanges ( ) ;
0 commit comments