Skip to content

Commit 6cec8cd

Browse files
committed
test(material/slide-toggle): fix test flakes
Attempts to fix some of the recent slide toggle test flakes by making all tests `fakeAsync` and adding the missing flushes. **Note:** while we don't technically need `fakeAsync` everywhere now that we know which tests are problematic, I did it anyway for consistency.
1 parent 0114ccd commit 6cec8cd

File tree

2 files changed

+144
-122
lines changed

2 files changed

+144
-122
lines changed

src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('MDC-based MatSlideToggle without forms', () => {
6363
labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement;
6464
}));
6565

66-
it('should apply class based on color attribute', () => {
66+
it('should apply class based on color attribute', fakeAsync(() => {
6767
testComponent.slideColor = 'primary';
6868
fixture.detectChanges();
6969

@@ -73,39 +73,40 @@ describe('MDC-based MatSlideToggle without forms', () => {
7373
fixture.detectChanges();
7474

7575
expect(slideToggleElement.classList).toContain('mat-accent');
76-
});
76+
}));
7777

78-
it('should correctly update the disabled property', () => {
78+
it('should correctly update the disabled property', fakeAsync(() => {
7979
expect(buttonElement.disabled).toBeFalsy();
8080

8181
testComponent.isDisabled = true;
8282
fixture.detectChanges();
8383

8484
expect(buttonElement.disabled).toBeTruthy();
85-
});
85+
}));
8686

87-
it('should correctly update the checked property', () => {
87+
it('should correctly update the checked property', fakeAsync(() => {
8888
expect(slideToggle.checked).toBeFalsy();
8989
expect(buttonElement.getAttribute('aria-checked')).toBe('false');
9090

9191
testComponent.slideChecked = true;
9292
fixture.detectChanges();
9393

9494
expect(buttonElement.getAttribute('aria-checked')).toBe('true');
95-
});
95+
}));
9696

97-
it('should set the toggle to checked on click', () => {
97+
it('should set the toggle to checked on click', fakeAsync(() => {
9898
expect(slideToggle.checked).toBe(false);
9999
expect(buttonElement.getAttribute('aria-checked')).toBe('false');
100100
expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked');
101101

102102
labelElement.click();
103103
fixture.detectChanges();
104+
flush();
104105

105106
expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked');
106107
expect(slideToggle.checked).toBe(true);
107108
expect(buttonElement.getAttribute('aria-checked')).toBe('true');
108-
});
109+
}));
109110

110111
it('should not trigger the click event multiple times', fakeAsync(() => {
111112
// By default, when clicking on a label element, a generated click will be dispatched
@@ -125,15 +126,16 @@ describe('MDC-based MatSlideToggle without forms', () => {
125126
expect(testComponent.onSlideClick).toHaveBeenCalledTimes(1);
126127
}));
127128

128-
it('should trigger the change event properly', () => {
129+
it('should trigger the change event properly', fakeAsync(() => {
129130
expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked');
130131

131132
labelElement.click();
132133
fixture.detectChanges();
134+
flush();
133135

134136
expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked');
135137
expect(testComponent.onSlideChange).toHaveBeenCalledTimes(1);
136-
});
138+
}));
137139

138140
it('should not trigger the change event by changing the native value', fakeAsync(() => {
139141
expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked');
@@ -147,7 +149,7 @@ describe('MDC-based MatSlideToggle without forms', () => {
147149
expect(testComponent.onSlideChange).not.toHaveBeenCalled();
148150
}));
149151

150-
it('should add a suffix to the element id', () => {
152+
it('should add a suffix to the element id', fakeAsync(() => {
151153
testComponent.slideId = 'myId';
152154
fixture.detectChanges();
153155

@@ -165,9 +167,9 @@ describe('MDC-based MatSlideToggle without forms', () => {
165167

166168
// Once the id binding is set to null, the id property should auto-generate a unique id.
167169
expect(buttonElement.id).toMatch(/mat-mdc-slide-toggle-\d+-button/);
168-
});
170+
}));
169171

170-
it('should forward the tabIndex to the underlying element', () => {
172+
it('should forward the tabIndex to the underlying element', fakeAsync(() => {
171173
fixture.detectChanges();
172174

173175
expect(buttonElement.tabIndex).toBe(0);
@@ -176,9 +178,9 @@ describe('MDC-based MatSlideToggle without forms', () => {
176178
fixture.detectChanges();
177179

178180
expect(buttonElement.tabIndex).toBe(4);
179-
});
181+
}));
180182

181-
it('should forward the specified name to the element', () => {
183+
it('should forward the specified name to the element', fakeAsync(() => {
182184
testComponent.slideName = 'myName';
183185
fixture.detectChanges();
184186

@@ -193,9 +195,9 @@ describe('MDC-based MatSlideToggle without forms', () => {
193195
fixture.detectChanges();
194196

195197
expect(buttonElement.name).toBe('');
196-
});
198+
}));
197199

198-
it('should forward the aria-label attribute to the element', () => {
200+
it('should forward the aria-label attribute to the element', fakeAsync(() => {
199201
testComponent.slideLabel = 'ariaLabel';
200202
fixture.detectChanges();
201203

@@ -205,9 +207,9 @@ describe('MDC-based MatSlideToggle without forms', () => {
205207
fixture.detectChanges();
206208

207209
expect(buttonElement.hasAttribute('aria-label')).toBeFalsy();
208-
});
210+
}));
209211

210-
it('should forward the aria-labelledby attribute to the element', () => {
212+
it('should forward the aria-labelledby attribute to the element', fakeAsync(() => {
211213
testComponent.slideLabelledBy = 'ariaLabelledBy';
212214
fixture.detectChanges();
213215

@@ -219,9 +221,9 @@ describe('MDC-based MatSlideToggle without forms', () => {
219221
// We fall back to pointing to the label if a value isn't provided.
220222
expect(buttonElement.getAttribute('aria-labelledby'))
221223
.toMatch(/mat-mdc-slide-toggle-label-\d+/);
222-
});
224+
}));
223225

224-
it('should forward the aria-describedby attribute to the element', () => {
226+
it('should forward the aria-describedby attribute to the element', fakeAsync(() => {
225227
testComponent.slideAriaDescribedBy = 'some-element';
226228
fixture.detectChanges();
227229

@@ -231,13 +233,13 @@ describe('MDC-based MatSlideToggle without forms', () => {
231233
fixture.detectChanges();
232234

233235
expect(buttonElement.hasAttribute('aria-describedby')).toBe(false);
234-
});
236+
}));
235237

236-
it('should set the `for` attribute to the id of the element', () => {
238+
it('should set the `for` attribute to the id of the element', fakeAsync(() => {
237239
expect(labelElement.getAttribute('for')).toBeTruthy();
238240
expect(buttonElement.getAttribute('id')).toBeTruthy();
239241
expect(labelElement.getAttribute('for')).toBe(buttonElement.getAttribute('id'));
240-
});
242+
}));
241243

242244
it('should emit the new values properly', fakeAsync(() => {
243245
labelElement.click();
@@ -274,14 +276,14 @@ describe('MDC-based MatSlideToggle without forms', () => {
274276
expect(buttonElement.getAttribute('aria-required')).toBe('false');
275277
});
276278

277-
it('should focus on underlying element when focus() is called', () => {
279+
it('should focus on underlying element when focus() is called', fakeAsync(() => {
278280
expect(document.activeElement).not.toBe(buttonElement);
279281

280282
slideToggle.focus();
281283
fixture.detectChanges();
282284

283285
expect(document.activeElement).toBe(buttonElement);
284-
});
286+
}));
285287

286288
it('should focus on underlying element when the host is focused', fakeAsync(() => {
287289
expect(document.activeElement).not.toBe(buttonElement);
@@ -308,7 +310,7 @@ describe('MDC-based MatSlideToggle without forms', () => {
308310
expect(document.activeElement).not.toBe(buttonElement);
309311
})));
310312

311-
it('should set a element class if labelPosition is set to before', () => {
313+
it('should set a element class if labelPosition is set to before', fakeAsync(() => {
312314
const formField = slideToggleElement.querySelector('.mdc-form-field')!;
313315

314316
expect(formField.classList).not.toContain('mdc-form-field--align-end');
@@ -317,9 +319,9 @@ describe('MDC-based MatSlideToggle without forms', () => {
317319
fixture.detectChanges();
318320

319321
expect(formField.classList).toContain('mdc-form-field--align-end');
320-
});
322+
}));
321323

322-
it('should show ripples', () => {
324+
it('should show ripples', fakeAsync(() => {
323325
const rippleSelector = '.mat-ripple-element';
324326
const switchElement = slideToggleElement.querySelector('.mdc-switch')!;
325327

@@ -329,9 +331,10 @@ describe('MDC-based MatSlideToggle without forms', () => {
329331
dispatchFakeEvent(switchElement, 'mouseup');
330332

331333
expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(1);
332-
});
334+
flush();
335+
}));
333336

334-
it('should not show ripples when disableRipple is set', () => {
337+
it('should not show ripples when disableRipple is set', fakeAsync(() => {
335338
const switchElement = slideToggleElement.querySelector('.mdc-switch')!;
336339
const rippleSelector = '.mat-ripple-element';
337340
testComponent.disableRipple = true;
@@ -343,12 +346,13 @@ describe('MDC-based MatSlideToggle without forms', () => {
343346
dispatchFakeEvent(switchElement, 'mouseup');
344347

345348
expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(0);
346-
});
349+
flush();
350+
}));
347351

348-
it('should have a focus indicator', () => {
352+
it('should have a focus indicator', fakeAsync(() => {
349353
const rippleElement = slideToggleElement.querySelector('.mat-mdc-slide-toggle-ripple')!;
350354
expect(rippleElement.classList).toContain('mat-mdc-focus-indicator');
351-
});
355+
}));
352356
});
353357

354358
describe('custom template', () => {
@@ -374,21 +378,21 @@ describe('MDC-based MatSlideToggle without forms', () => {
374378
.toBe(5);
375379
}));
376380

377-
it('should add the disabled class if disabled through attribute', () => {
381+
it('should add the disabled class if disabled through attribute', fakeAsync(() => {
378382
const fixture = TestBed.createComponent(SlideToggleCheckedAndDisabledAttr);
379383
fixture.detectChanges();
380384

381385
const switchEl = fixture.nativeElement.querySelector('.mdc-switch');
382386
expect(switchEl.classList).toContain('mdc-switch--disabled');
383-
});
387+
}));
384388

385-
it('should add the checked class if checked through attribute', () => {
389+
it('should add the checked class if checked through attribute', fakeAsync(() => {
386390
const fixture = TestBed.createComponent(SlideToggleCheckedAndDisabledAttr);
387391
fixture.detectChanges();
388392

389393
const switchEl = fixture.nativeElement.querySelector('.mdc-switch');
390394
expect(switchEl.classList).toContain('mdc-switch--checked');
391-
});
395+
}));
392396

393397
it('should set the tabindex of the host element to -1', fakeAsync(() => {
394398
const fixture = TestBed.createComponent(SlideToggleWithTabindexAttr);
@@ -452,7 +456,7 @@ describe('MDC-based MatSlideToggle without forms', () => {
452456
expect(testComponent.dragTriggered).toBe(0);
453457
}));
454458

455-
it('should be able to change the default color', () => {
459+
it('should be able to change the default color', fakeAsync(() => {
456460
TestBed
457461
.resetTestingModule()
458462
.configureTestingModule({
@@ -466,16 +470,16 @@ describe('MDC-based MatSlideToggle without forms', () => {
466470
fixture.detectChanges();
467471
const slideToggle = fixture.nativeElement.querySelector('.mat-mdc-slide-toggle');
468472
expect(slideToggle.classList).toContain('mat-warn');
469-
});
473+
}));
470474

471-
it('should clear static aria attributes from the host node', () => {
475+
it('should clear static aria attributes from the host node', fakeAsync(() => {
472476
const fixture = TestBed.createComponent(SlideToggleWithStaticAriaAttributes);
473477
fixture.detectChanges();
474478

475479
const host: HTMLElement = fixture.nativeElement.querySelector('mat-slide-toggle');
476480
expect(host.hasAttribute('aria-label')).toBe(false);
477481
expect(host.hasAttribute('aria-labelledby')).toBe(false);
478-
});
482+
}));
479483
});
480484

481485
describe('MDC-based MatSlideToggle with forms', () => {
@@ -519,10 +523,10 @@ describe('MDC-based MatSlideToggle with forms', () => {
519523
labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement;
520524
}));
521525

522-
it('should be initially set to ng-pristine', () => {
526+
it('should be initially set to ng-pristine', fakeAsync(() => {
523527
expect(slideToggleElement.classList).toContain('ng-pristine');
524528
expect(slideToggleElement.classList).not.toContain('ng-dirty');
525-
});
529+
}));
526530

527531
it('should update the model programmatically', fakeAsync(() => {
528532
expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked');
@@ -549,6 +553,7 @@ describe('MDC-based MatSlideToggle with forms', () => {
549553

550554
dispatchFakeEvent(buttonElement, 'focus');
551555
buttonElement.click();
556+
flush();
552557

553558
expect(slideToggleModel.valid).toBe(true);
554559
expect(slideToggleModel.pristine).toBe(false);
@@ -682,16 +687,16 @@ describe('MDC-based MatSlideToggle with forms', () => {
682687
let slideToggle: MatSlideToggle;
683688
let buttonElement: HTMLButtonElement;
684689

685-
beforeEach(() => {
690+
beforeEach(fakeAsync(() => {
686691
fixture = TestBed.createComponent(SlideToggleWithFormControl);
687692
fixture.detectChanges();
688693

689694
testComponent = fixture.debugElement.componentInstance;
690695
slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.componentInstance;
691696
buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement;
692-
});
697+
}));
693698

694-
it('should toggle the disabled state', () => {
699+
it('should toggle the disabled state', fakeAsync(() => {
695700
expect(slideToggle.disabled).toBe(false);
696701
expect(buttonElement.disabled).toBe(false);
697702

@@ -706,7 +711,7 @@ describe('MDC-based MatSlideToggle with forms', () => {
706711

707712
expect(slideToggle.disabled).toBe(false);
708713
expect(buttonElement.disabled).toBe(false);
709-
});
714+
}));
710715
});
711716

712717
describe('with form element', () => {
@@ -723,16 +728,17 @@ describe('MDC-based MatSlideToggle with forms', () => {
723728
buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement;
724729
}));
725730

726-
it('should not submit the form when clicked', () => {
731+
it('should not submit the form when clicked', fakeAsync(() => {
727732
expect(testComponent.isSubmitted).toBe(false);
728733

729734
buttonElement.click();
730735
fixture.detectChanges();
736+
flush();
731737

732738
expect(testComponent.isSubmitted).toBe(false);
733-
});
739+
}));
734740

735-
it('should have proper invalid state if unchecked', () => {
741+
it('should have proper invalid state if unchecked', fakeAsync(() => {
736742
testComponent.isRequired = true;
737743
fixture.detectChanges();
738744

@@ -745,6 +751,7 @@ describe('MDC-based MatSlideToggle with forms', () => {
745751
// should become valid.
746752
buttonElement.click();
747753
fixture.detectChanges();
754+
flush();
748755

749756
expect(slideToggleEl.classList).not.toContain('ng-invalid');
750757
expect(slideToggleEl.classList).toContain('ng-valid');
@@ -753,14 +760,15 @@ describe('MDC-based MatSlideToggle with forms', () => {
753760
// should become invalid.
754761
buttonElement.click();
755762
fixture.detectChanges();
763+
flush();
756764

757765
expect(slideToggleEl.classList).toContain('ng-invalid');
758766
expect(slideToggleEl.classList).not.toContain('ng-valid');
759-
});
767+
}));
760768
});
761769

762770
describe('with model and change event', () => {
763-
it('should report changes to NgModel before emitting change event', () => {
771+
it('should report changes to NgModel before emitting change event', fakeAsync(() => {
764772
const fixture = TestBed.createComponent(SlideToggleWithModelAndChangeEvent);
765773
fixture.detectChanges();
766774

@@ -773,9 +781,10 @@ describe('MDC-based MatSlideToggle with forms', () => {
773781
});
774782

775783
labelEl.click();
784+
flush();
776785

777786
expect(fixture.componentInstance.onChange).toHaveBeenCalledTimes(1);
778-
});
787+
}));
779788
});
780789
});
781790

0 commit comments

Comments
 (0)