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' ;
8
+ import { MutationObserverFactory } from '@angular/cdk/observers' ;
15
9
16
10
17
11
describe ( 'MatCheckbox' , ( ) => {
18
12
let fixture : ComponentFixture < any > ;
19
13
20
- beforeEach ( async ( ( ) => {
14
+ beforeEach ( fakeAsync ( ( ) => {
21
15
TestBed . configureTestingModule ( {
22
16
imports : [ MatCheckboxModule , FormsModule , ReactiveFormsModule ] ,
23
17
declarations : [
@@ -115,7 +109,7 @@ describe('MatCheckbox', () => {
115
109
fixture . detectChanges ( ) ;
116
110
117
111
// Flush the microtasks because the forms module updates the model state asynchronously.
118
- flushMicrotasks ( ) ;
112
+ flush ( ) ;
119
113
120
114
// The checked property has been updated from the model and now the view needs
121
115
// to reflect the state change.
@@ -140,7 +134,7 @@ describe('MatCheckbox', () => {
140
134
fixture . detectChanges ( ) ;
141
135
142
136
// Flush the microtasks because the forms module updates the model state asynchronously.
143
- flushMicrotasks ( ) ;
137
+ flush ( ) ;
144
138
145
139
// The checked property has been updated from the model and now the view needs
146
140
// to reflect the state change.
@@ -152,7 +146,7 @@ describe('MatCheckbox', () => {
152
146
expect ( testComponent . isIndeterminate ) . toBe ( false ) ;
153
147
} ) ) ;
154
148
155
- it ( 'should not set indeterminate to false when checked is set programmatically' , async ( ( ) => {
149
+ it ( 'should not set indeterminate to false when checked is set programmatically' , ( ) => {
156
150
testComponent . isIndeterminate = true ;
157
151
fixture . detectChanges ( ) ;
158
152
@@ -175,7 +169,7 @@ describe('MatCheckbox', () => {
175
169
expect ( inputElement . indeterminate ) . toBe ( true ) ;
176
170
expect ( inputElement . checked ) . toBe ( false ) ;
177
171
expect ( testComponent . isIndeterminate ) . toBe ( true ) ;
178
- } ) ) ;
172
+ } ) ;
179
173
180
174
it ( 'should change native element checked when check programmatically' , ( ) => {
181
175
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -211,7 +205,7 @@ describe('MatCheckbox', () => {
211
205
checkboxInstance . _onInputClick ( < Event > { stopPropagation : ( ) => { } } ) ;
212
206
213
207
// Flush the microtasks because the indeterminate state will be updated in the next tick.
214
- flushMicrotasks ( ) ;
208
+ flush ( ) ;
215
209
216
210
expect ( checkboxInstance . checked ) . toBe ( true ) ;
217
211
expect ( checkboxInstance . indeterminate ) . toBe ( false ) ;
@@ -261,7 +255,7 @@ describe('MatCheckbox', () => {
261
255
fixture . detectChanges ( ) ;
262
256
263
257
// Flush the microtasks because the indeterminate state will be updated in the next tick.
264
- flushMicrotasks ( ) ;
258
+ flush ( ) ;
265
259
266
260
expect ( checkboxInstance . checked ) . toBe ( true ) ;
267
261
expect ( checkboxInstance . indeterminate ) . toBe ( false ) ;
@@ -316,7 +310,7 @@ describe('MatCheckbox', () => {
316
310
expect ( testComponent . onCheckboxClick ) . toHaveBeenCalledTimes ( 1 ) ;
317
311
} ) ;
318
312
319
- it ( 'should trigger a change event when the native input does' , async ( ( ) => {
313
+ it ( 'should trigger a change event when the native input does' , fakeAsync ( ( ) => {
320
314
spyOn ( testComponent , 'onCheckboxChange' ) ;
321
315
322
316
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -328,16 +322,15 @@ describe('MatCheckbox', () => {
328
322
expect ( inputElement . checked ) . toBe ( true ) ;
329
323
expect ( checkboxNativeElement . classList ) . toContain ( 'mat-checkbox-checked' ) ;
330
324
331
- // Wait for the fixture to become stable, because the EventEmitter for the change event,
332
- // will only fire after the zone async change detection has finished.
333
- fixture . whenStable ( ) . then ( ( ) => {
334
- // The change event shouldn't fire, because the value change was not caused
335
- // by any interaction.
336
- expect ( testComponent . onCheckboxChange ) . toHaveBeenCalledTimes ( 1 ) ;
337
- } ) ;
325
+ fixture . detectChanges ( ) ;
326
+ flush ( ) ;
327
+
328
+ // The change event shouldn't fire, because the value change was not caused
329
+ // by any interaction.
330
+ expect ( testComponent . onCheckboxChange ) . toHaveBeenCalledTimes ( 1 ) ;
338
331
} ) ) ;
339
332
340
- it ( 'should not trigger the change event by changing the native value' , async ( ( ) => {
333
+ it ( 'should not trigger the change event by changing the native value' , fakeAsync ( ( ) => {
341
334
spyOn ( testComponent , 'onCheckboxChange' ) ;
342
335
343
336
expect ( inputElement . checked ) . toBe ( false ) ;
@@ -349,14 +342,12 @@ describe('MatCheckbox', () => {
349
342
expect ( inputElement . checked ) . toBe ( true ) ;
350
343
expect ( checkboxNativeElement . classList ) . toContain ( 'mat-checkbox-checked' ) ;
351
344
352
- // Wait for the fixture to become stable, because the EventEmitter for the change event,
353
- // will only fire after the zone async change detection has finished.
354
- fixture . whenStable ( ) . then ( ( ) => {
355
- // The change event shouldn't fire, because the value change was not caused
356
- // by any interaction.
357
- expect ( testComponent . onCheckboxChange ) . not . toHaveBeenCalled ( ) ;
358
- } ) ;
345
+ fixture . detectChanges ( ) ;
346
+ flush ( ) ;
359
347
348
+ // The change event shouldn't fire, because the value change was not caused
349
+ // by any interaction.
350
+ expect ( testComponent . onCheckboxChange ) . not . toHaveBeenCalled ( ) ;
360
351
} ) ) ;
361
352
362
353
it ( 'should forward the required attribute' , ( ) => {
@@ -582,22 +573,20 @@ describe('MatCheckbox', () => {
582
573
expect ( changeSpy ) . toHaveBeenCalledTimes ( 1 ) ;
583
574
} ) ;
584
575
585
- it ( 'should not emit a DOM event to the change output' , async ( ( ) => {
576
+ it ( 'should not emit a DOM event to the change output' , fakeAsync ( ( ) => {
586
577
fixture . detectChanges ( ) ;
587
578
expect ( testComponent . lastEvent ) . toBeUndefined ( ) ;
588
579
589
580
// Trigger the click on the inputElement, because the input will probably
590
581
// emit a DOM event to the change output.
591
582
inputElement . click ( ) ;
592
583
fixture . detectChanges ( ) ;
584
+ flush ( ) ;
593
585
594
- fixture . whenStable ( ) . then ( ( ) => {
595
- // We're checking the arguments type / emitted value to be a boolean, because sometimes the
596
- // emitted value can be a DOM Event, which is not valid.
597
- // See angular/angular#4059
598
- expect ( testComponent . lastEvent . checked ) . toBe ( true ) ;
599
- } ) ;
600
-
586
+ // We're checking the arguments type / emitted value to be a boolean, because sometimes the
587
+ // emitted value can be a DOM Event, which is not valid.
588
+ // See angular/angular#4059
589
+ expect ( testComponent . lastEvent . checked ) . toBe ( true ) ;
601
590
} ) ) ;
602
591
} ) ;
603
592
@@ -682,7 +671,7 @@ describe('MatCheckbox', () => {
682
671
683
672
describe ( 'with native tabindex attribute' , ( ) => {
684
673
685
- it ( 'should properly detect native tabindex attribute' , async ( ( ) => {
674
+ it ( 'should properly detect native tabindex attribute' , fakeAsync ( ( ) => {
686
675
fixture = TestBed . createComponent ( CheckboxWithTabindexAttr ) ;
687
676
fixture . detectChanges ( ) ;
688
677
@@ -728,7 +717,7 @@ describe('MatCheckbox', () => {
728
717
} ) ;
729
718
730
719
it ( 'should be in pristine, untouched, and valid states initially' , fakeAsync ( ( ) => {
731
- flushMicrotasks ( ) ;
720
+ flush ( ) ;
732
721
733
722
let checkboxElement = fixture . debugElement . query ( By . directive ( MatCheckbox ) ) ;
734
723
let ngModel = checkboxElement . injector . get < NgModel > ( NgModel ) ;
@@ -861,35 +850,63 @@ describe('MatCheckbox', () => {
861
850
. toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
862
851
} ) ;
863
852
864
- it ( 'should not remove margin if initial label is set through binding' , async ( ( ) => {
853
+ it ( 'should not remove margin if initial label is set through binding' , ( ) => {
865
854
testComponent . label = 'Some content' ;
866
855
fixture . detectChanges ( ) ;
867
856
868
857
expect ( checkboxInnerContainer . classList )
869
858
. not . toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
870
- } ) ) ;
859
+ } ) ;
860
+
861
+ it ( 'should re-add margin if label is added asynchronously' , ( ) => {
862
+ fixture . destroy ( ) ;
863
+
864
+ const mutationCallbacks : Function [ ] = [ ] ;
865
+
866
+ TestBed
867
+ . resetTestingModule ( )
868
+ . configureTestingModule ( {
869
+ imports : [ MatCheckboxModule , FormsModule , ReactiveFormsModule ] ,
870
+ declarations : [ CheckboxWithoutLabel ] ,
871
+ providers : [ {
872
+ provide : MutationObserverFactory ,
873
+ useValue : {
874
+ // Stub out the factory that creates mutation observers for the underlying directive
875
+ // to allows us to flush out the callbacks asynchronously.
876
+ create : ( callback : Function ) => {
877
+ mutationCallbacks . push ( callback ) ;
878
+
879
+ return {
880
+ observe : ( ) => { } ,
881
+ disconnect : ( ) => { }
882
+ } ;
883
+ }
884
+ }
885
+ } ]
886
+ } )
887
+ . compileComponents ( ) ;
888
+
889
+ fixture = TestBed . createComponent ( CheckboxWithoutLabel ) ;
890
+ checkboxInnerContainer = fixture . debugElement
891
+ . query ( By . css ( '.mat-checkbox-inner-container' ) ) . nativeElement ;
871
892
872
- it ( 'should re-add margin if label is added asynchronously' , async ( ( ) => {
873
893
fixture . detectChanges ( ) ;
874
894
875
895
expect ( checkboxInnerContainer . classList )
876
896
. toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
877
897
878
- testComponent . label = 'Some content' ;
898
+ fixture . componentInstance . label = 'Some content' ;
879
899
fixture . detectChanges ( ) ;
900
+ mutationCallbacks . forEach ( callback => callback ( ) ) ;
880
901
881
- // Wait for the MutationObserver to detect the content change and for the cdkObserveContent
882
- // to emit the change event to the checkbox.
883
- setTimeout ( ( ) => {
884
- // The MutationObserver from the cdkObserveContent directive detected the content change
885
- // and notified the checkbox component. The checkbox then marks the component as dirty
886
- // by calling `markForCheck()`. This needs to be reflected by the component template then.
887
- fixture . detectChanges ( ) ;
902
+ // The MutationObserver from the cdkObserveContent directive detected the content change
903
+ // and notified the checkbox component. The checkbox then marks the component as dirty
904
+ // by calling `markForCheck()`. This needs to be reflected by the component template then.
905
+ fixture . detectChanges ( ) ;
888
906
889
- expect ( checkboxInnerContainer . classList )
890
- . not . toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
891
- } , 1 ) ;
892
- } ) ) ;
907
+ expect ( checkboxInnerContainer . classList )
908
+ . not . toContain ( 'mat-checkbox-inner-container-no-side-margin' ) ;
909
+ } ) ;
893
910
894
911
it ( 'should not add the "name" attribute if it is not passed in' , ( ) => {
895
912
fixture . detectChanges ( ) ;
0 commit comments