Skip to content

Commit 675d82e

Browse files
authored
test(material/slide-toggle): fix test flakes (#23332)
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 675d82e

File tree

2 files changed

+147
-122
lines changed

2 files changed

+147
-122
lines changed

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

Lines changed: 64 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,15 @@ 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();
284+
flush();
282285

283286
expect(document.activeElement).toBe(buttonElement);
284-
});
287+
}));
285288

286289
it('should focus on underlying element when the host is focused', fakeAsync(() => {
287290
expect(document.activeElement).not.toBe(buttonElement);
@@ -308,7 +311,7 @@ describe('MDC-based MatSlideToggle without forms', () => {
308311
expect(document.activeElement).not.toBe(buttonElement);
309312
})));
310313

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

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

319322
expect(formField.classList).toContain('mdc-form-field--align-end');
320-
});
323+
}));
321324

322-
it('should show ripples', () => {
325+
it('should show ripples', fakeAsync(() => {
323326
const rippleSelector = '.mat-ripple-element';
324327
const switchElement = slideToggleElement.querySelector('.mdc-switch')!;
325328

@@ -329,9 +332,10 @@ describe('MDC-based MatSlideToggle without forms', () => {
329332
dispatchFakeEvent(switchElement, 'mouseup');
330333

331334
expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(1);
332-
});
335+
flush();
336+
}));
333337

334-
it('should not show ripples when disableRipple is set', () => {
338+
it('should not show ripples when disableRipple is set', fakeAsync(() => {
335339
const switchElement = slideToggleElement.querySelector('.mdc-switch')!;
336340
const rippleSelector = '.mat-ripple-element';
337341
testComponent.disableRipple = true;
@@ -343,12 +347,13 @@ describe('MDC-based MatSlideToggle without forms', () => {
343347
dispatchFakeEvent(switchElement, 'mouseup');
344348

345349
expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(0);
346-
});
350+
flush();
351+
}));
347352

348-
it('should have a focus indicator', () => {
353+
it('should have a focus indicator', fakeAsync(() => {
349354
const rippleElement = slideToggleElement.querySelector('.mat-mdc-slide-toggle-ripple')!;
350355
expect(rippleElement.classList).toContain('mat-mdc-focus-indicator');
351-
});
356+
}));
352357
});
353358

354359
describe('custom template', () => {
@@ -374,21 +379,21 @@ describe('MDC-based MatSlideToggle without forms', () => {
374379
.toBe(5);
375380
}));
376381

377-
it('should add the disabled class if disabled through attribute', () => {
382+
it('should add the disabled class if disabled through attribute', fakeAsync(() => {
378383
const fixture = TestBed.createComponent(SlideToggleCheckedAndDisabledAttr);
379384
fixture.detectChanges();
380385

381386
const switchEl = fixture.nativeElement.querySelector('.mdc-switch');
382387
expect(switchEl.classList).toContain('mdc-switch--disabled');
383-
});
388+
}));
384389

385-
it('should add the checked class if checked through attribute', () => {
390+
it('should add the checked class if checked through attribute', fakeAsync(() => {
386391
const fixture = TestBed.createComponent(SlideToggleCheckedAndDisabledAttr);
387392
fixture.detectChanges();
388393

389394
const switchEl = fixture.nativeElement.querySelector('.mdc-switch');
390395
expect(switchEl.classList).toContain('mdc-switch--checked');
391-
});
396+
}));
392397

393398
it('should set the tabindex of the host element to -1', fakeAsync(() => {
394399
const fixture = TestBed.createComponent(SlideToggleWithTabindexAttr);
@@ -452,7 +457,7 @@ describe('MDC-based MatSlideToggle without forms', () => {
452457
expect(testComponent.dragTriggered).toBe(0);
453458
}));
454459

455-
it('should be able to change the default color', () => {
460+
it('should be able to change the default color', fakeAsync(() => {
456461
TestBed
457462
.resetTestingModule()
458463
.configureTestingModule({
@@ -466,16 +471,16 @@ describe('MDC-based MatSlideToggle without forms', () => {
466471
fixture.detectChanges();
467472
const slideToggle = fixture.nativeElement.querySelector('.mat-mdc-slide-toggle');
468473
expect(slideToggle.classList).toContain('mat-warn');
469-
});
474+
}));
470475

471-
it('should clear static aria attributes from the host node', () => {
476+
it('should clear static aria attributes from the host node', fakeAsync(() => {
472477
const fixture = TestBed.createComponent(SlideToggleWithStaticAriaAttributes);
473478
fixture.detectChanges();
474479

475480
const host: HTMLElement = fixture.nativeElement.querySelector('mat-slide-toggle');
476481
expect(host.hasAttribute('aria-label')).toBe(false);
477482
expect(host.hasAttribute('aria-labelledby')).toBe(false);
478-
});
483+
}));
479484
});
480485

481486
describe('MDC-based MatSlideToggle with forms', () => {
@@ -519,10 +524,10 @@ describe('MDC-based MatSlideToggle with forms', () => {
519524
labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement;
520525
}));
521526

522-
it('should be initially set to ng-pristine', () => {
527+
it('should be initially set to ng-pristine', fakeAsync(() => {
523528
expect(slideToggleElement.classList).toContain('ng-pristine');
524529
expect(slideToggleElement.classList).not.toContain('ng-dirty');
525-
});
530+
}));
526531

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

550555
dispatchFakeEvent(buttonElement, 'focus');
551556
buttonElement.click();
557+
flush();
552558

553559
expect(slideToggleModel.valid).toBe(true);
554560
expect(slideToggleModel.pristine).toBe(false);
@@ -682,16 +688,16 @@ describe('MDC-based MatSlideToggle with forms', () => {
682688
let slideToggle: MatSlideToggle;
683689
let buttonElement: HTMLButtonElement;
684690

685-
beforeEach(() => {
691+
beforeEach(fakeAsync(() => {
686692
fixture = TestBed.createComponent(SlideToggleWithFormControl);
687693
fixture.detectChanges();
688694

689695
testComponent = fixture.debugElement.componentInstance;
690696
slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.componentInstance;
691697
buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement;
692-
});
698+
}));
693699

694-
it('should toggle the disabled state', () => {
700+
it('should toggle the disabled state', fakeAsync(() => {
695701
expect(slideToggle.disabled).toBe(false);
696702
expect(buttonElement.disabled).toBe(false);
697703

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

707713
expect(slideToggle.disabled).toBe(false);
708714
expect(buttonElement.disabled).toBe(false);
709-
});
715+
}));
710716
});
711717

712718
describe('with form element', () => {
@@ -723,16 +729,17 @@ describe('MDC-based MatSlideToggle with forms', () => {
723729
buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement;
724730
}));
725731

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

729735
buttonElement.click();
730736
fixture.detectChanges();
737+
flush();
731738

732739
expect(testComponent.isSubmitted).toBe(false);
733-
});
740+
}));
734741

735-
it('should have proper invalid state if unchecked', () => {
742+
it('should have proper invalid state if unchecked', fakeAsync(() => {
736743
testComponent.isRequired = true;
737744
fixture.detectChanges();
738745

@@ -745,6 +752,7 @@ describe('MDC-based MatSlideToggle with forms', () => {
745752
// should become valid.
746753
buttonElement.click();
747754
fixture.detectChanges();
755+
flush();
748756

749757
expect(slideToggleEl.classList).not.toContain('ng-invalid');
750758
expect(slideToggleEl.classList).toContain('ng-valid');
@@ -753,14 +761,15 @@ describe('MDC-based MatSlideToggle with forms', () => {
753761
// should become invalid.
754762
buttonElement.click();
755763
fixture.detectChanges();
764+
flush();
756765

757766
expect(slideToggleEl.classList).toContain('ng-invalid');
758767
expect(slideToggleEl.classList).not.toContain('ng-valid');
759-
});
768+
}));
760769
});
761770

762771
describe('with model and change event', () => {
763-
it('should report changes to NgModel before emitting change event', () => {
772+
it('should report changes to NgModel before emitting change event', fakeAsync(() => {
764773
const fixture = TestBed.createComponent(SlideToggleWithModelAndChangeEvent);
765774
fixture.detectChanges();
766775

@@ -773,9 +782,10 @@ describe('MDC-based MatSlideToggle with forms', () => {
773782
});
774783

775784
labelEl.click();
785+
flush();
776786

777787
expect(fixture.componentInstance.onChange).toHaveBeenCalledTimes(1);
778-
});
788+
}));
779789
});
780790
});
781791

0 commit comments

Comments
 (0)