Skip to content

Commit 8228c5f

Browse files
crisbetojelbourn
authored andcommitted
refactor(tabs): switch to fakeAsync tests (#10263)
* Reworks the tabs unit tests to run in the `fakeAsync` zone, rather than `async`. This has the advantage of being quicker, cleaner and avoiding timeouts in IE and Edge when the browser isn't focused. * Fixes a few unit tests that weren't asserting correctly, because their assertions were inside `whenStable` promises that weren't resolving.
1 parent a943b36 commit 8228c5f

File tree

2 files changed

+74
-75
lines changed

2 files changed

+74
-75
lines changed

src/lib/tabs/tab-body.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,8 @@ export class MatTabBodyPortal extends CdkPortalOutlet implements OnInit, OnDestr
7777
this.attach(this._host._content);
7878
}
7979
this._centeringSub = this._host._beforeCentering.subscribe((isCentering: boolean) => {
80-
if (isCentering) {
81-
if (!this.hasAttached()) {
82-
this.attach(this._host._content);
83-
}
80+
if (isCentering && !this.hasAttached()) {
81+
this.attach(this._host._content);
8482
}
8583
});
8684

src/lib/tabs/tab-group.spec.ts

Lines changed: 72 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {MatTab, MatTabGroup, MatTabHeaderPosition, MatTabsModule} from './index'
88

99

1010
describe('MatTabGroup', () => {
11-
beforeEach(async(() => {
11+
beforeEach(fakeAsync(() => {
1212
TestBed.configureTestingModule({
1313
imports: [MatTabsModule, NoopAnimationsModule],
1414
declarations: [
@@ -59,21 +59,21 @@ describe('MatTabGroup', () => {
5959
checkSelectedIndex(2, fixture);
6060
});
6161

62-
it('should support two-way binding for selectedIndex', async(() => {
62+
it('should support two-way binding for selectedIndex', fakeAsync(() => {
6363
let component = fixture.componentInstance;
6464
component.selectedIndex = 0;
6565

6666
fixture.detectChanges();
6767

6868
let tabLabel = fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1];
6969
tabLabel.nativeElement.click();
70-
7170
fixture.detectChanges();
72-
fixture.whenStable().then(() => {
73-
expect(component.selectedIndex).toBe(1);
74-
});
71+
tick();
72+
73+
expect(component.selectedIndex).toBe(1);
7574
}));
7675

76+
// Note: needs to be `async` in order to fail when we expect it to.
7777
it('should set to correct tab on fast change', async(() => {
7878
let component = fixture.componentInstance;
7979
component.selectedIndex = 0;
@@ -255,125 +255,125 @@ describe('MatTabGroup', () => {
255255
describe('dynamic binding tabs', () => {
256256
let fixture: ComponentFixture<SimpleDynamicTabsTestApp>;
257257

258-
beforeEach(async(() => {
258+
beforeEach(fakeAsync(() => {
259259
fixture = TestBed.createComponent(SimpleDynamicTabsTestApp);
260260
fixture.detectChanges();
261+
tick();
262+
fixture.detectChanges();
261263
}));
262264

263-
it('should be able to add a new tab, select it, and have correct origin position', () => {
264-
fixture.detectChanges();
265-
const component: MatTabGroup =
266-
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
265+
it('should be able to add a new tab, select it, and have correct origin position',
266+
fakeAsync(() => {
267+
const component: MatTabGroup =
268+
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
267269

268-
let tabs: MatTab[] = component._tabs.toArray();
269-
expect(tabs[0].origin).toBe(null);
270-
expect(tabs[1].origin).toBe(0);
271-
expect(tabs[2].origin).toBe(null);
270+
let tabs: MatTab[] = component._tabs.toArray();
271+
expect(tabs[0].origin).toBe(null);
272+
expect(tabs[1].origin).toBe(0);
273+
expect(tabs[2].origin).toBe(null);
272274

273-
// Add a new tab on the right and select it, expect an origin >= than 0 (animate right)
274-
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to right of index'});
275-
fixture.componentInstance.selectedIndex = 4;
276-
fixture.detectChanges();
275+
// Add a new tab on the right and select it, expect an origin >= than 0 (animate right)
276+
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to right of index'});
277+
fixture.componentInstance.selectedIndex = 4;
278+
fixture.detectChanges();
279+
tick();
277280

278-
tabs = component._tabs.toArray();
279-
expect(tabs[3].origin).toBeGreaterThanOrEqual(0);
281+
tabs = component._tabs.toArray();
282+
expect(tabs[3].origin).toBeGreaterThanOrEqual(0);
280283

281-
// Add a new tab in the beginning and select it, expect an origin < than 0 (animate left)
282-
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'});
283-
fixture.componentInstance.selectedIndex = 0;
284-
fixture.detectChanges();
284+
// Add a new tab in the beginning and select it, expect an origin < than 0 (animate left)
285+
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'});
286+
fixture.componentInstance.selectedIndex = 0;
287+
fixture.detectChanges();
288+
tick();
285289

286-
tabs = component._tabs.toArray();
287-
expect(tabs[0].origin).toBeLessThan(0);
288-
});
290+
tabs = component._tabs.toArray();
291+
expect(tabs[0].origin).toBeLessThan(0);
292+
}));
289293

290294

291-
it('should update selected index if the last tab removed while selected', () => {
292-
fixture.detectChanges();
295+
it('should update selected index if the last tab removed while selected', fakeAsync(() => {
293296
const component: MatTabGroup =
294297
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
295298

296299
const numberOfTabs = component._tabs.length;
297300
fixture.componentInstance.selectedIndex = numberOfTabs - 1;
298301
fixture.detectChanges();
302+
tick();
299303

300304
// Remove last tab while last tab is selected, expect next tab over to be selected
301305
fixture.componentInstance.tabs.pop();
302306
fixture.detectChanges();
307+
tick();
303308

304309
expect(component.selectedIndex).toBe(numberOfTabs - 2);
305-
});
310+
}));
306311

307312
});
308313

309314
describe('async tabs', () => {
310315
let fixture: ComponentFixture<AsyncTabsTestApp>;
311316

312-
it('should show tabs when they are available', async(() => {
317+
it('should show tabs when they are available', fakeAsync(() => {
313318
fixture = TestBed.createComponent(AsyncTabsTestApp);
314319

315-
let labels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
316-
317-
expect(labels.length).toBe(0);
320+
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(0);
318321

319322
fixture.detectChanges();
323+
tick();
324+
fixture.detectChanges();
325+
tick();
320326

321-
fixture.whenStable().then(() => {
322-
fixture.detectChanges();
323-
labels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
324-
expect(labels.length).toBe(2);
325-
});
327+
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(2);
326328
}));
327329
});
328330

329331
describe('with simple api', () => {
330332
let fixture: ComponentFixture<TabGroupWithSimpleApi>;
331333
let tabGroup: MatTabGroup;
332334

333-
beforeEach(() => {
335+
beforeEach(fakeAsync(() => {
334336
fixture = TestBed.createComponent(TabGroupWithSimpleApi);
335337
fixture.detectChanges();
338+
tick();
336339

337340
tabGroup =
338341
fixture.debugElement.query(By.directive(MatTabGroup)).componentInstance as MatTabGroup;
339-
});
342+
}));
340343

341-
it('should support a tab-group with the simple api', async(() => {
344+
it('should support a tab-group with the simple api', fakeAsync(() => {
342345
expect(getSelectedLabel(fixture).textContent).toMatch('Junk food');
343346
expect(getSelectedContent(fixture).textContent).toMatch('Pizza, fries');
344347

345348
tabGroup.selectedIndex = 2;
346349
fixture.detectChanges();
347-
// Use whenStable to wait for async observables and change detection to run in content.
348-
fixture.whenStable().then(() => {
350+
tick();
349351

350-
expect(getSelectedLabel(fixture).textContent).toMatch('Fruit');
351-
expect(getSelectedContent(fixture).textContent).toMatch('Apples, grapes');
352+
expect(getSelectedLabel(fixture).textContent).toMatch('Fruit');
353+
expect(getSelectedContent(fixture).textContent).toMatch('Apples, grapes');
352354

353-
fixture.componentInstance.otherLabel = 'Chips';
354-
fixture.componentInstance.otherContent = 'Salt, vinegar';
355-
fixture.detectChanges();
355+
fixture.componentInstance.otherLabel = 'Chips';
356+
fixture.componentInstance.otherContent = 'Salt, vinegar';
357+
fixture.detectChanges();
356358

357-
expect(getSelectedLabel(fixture).textContent).toMatch('Chips');
358-
expect(getSelectedContent(fixture).textContent).toMatch('Salt, vinegar');
359-
});
359+
expect(getSelectedLabel(fixture).textContent).toMatch('Chips');
360+
expect(getSelectedContent(fixture).textContent).toMatch('Salt, vinegar');
360361
}));
361362

362363
it('should support @ViewChild in the tab content', () => {
363364
expect(fixture.componentInstance.legumes).toBeTruthy();
364365
});
365366

366-
it('should only have the active tab in the DOM', async(() => {
367+
it('should only have the active tab in the DOM', fakeAsync(() => {
367368
expect(fixture.nativeElement.textContent).toContain('Pizza, fries');
368369
expect(fixture.nativeElement.textContent).not.toContain('Peanuts');
369370

370371
tabGroup.selectedIndex = 3;
371372
fixture.detectChanges();
372-
// Use whenStable to wait for async observables and change detection to run in content.
373-
fixture.whenStable().then(() => {
374-
expect(fixture.nativeElement.textContent).not.toContain('Pizza, fries');
375-
expect(fixture.nativeElement.textContent).toContain('Peanuts');
376-
});
373+
tick();
374+
375+
expect(fixture.nativeElement.textContent).not.toContain('Pizza, fries');
376+
expect(fixture.nativeElement.textContent).toContain('Peanuts');
377377
}));
378378

379379
it('should support setting the header position', () => {
@@ -389,20 +389,20 @@ describe('MatTabGroup', () => {
389389
});
390390

391391
describe('lazy loaded tabs', () => {
392-
it('should lazy load the second tab', async () => {
393-
let fixture = TestBed.createComponent(TemplateTabs);
392+
it('should lazy load the second tab', fakeAsync(() => {
393+
const fixture = TestBed.createComponent(TemplateTabs);
394394
fixture.detectChanges();
395+
tick();
395396

396-
let secondLabel = fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1];
397+
const secondLabel = fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1];
397398
secondLabel.nativeElement.click();
398399
fixture.detectChanges();
400+
tick();
401+
fixture.detectChanges();
399402

400-
fixture.whenStable().then(() => {
401-
fixture.detectChanges();
402-
let child = fixture.debugElement.query(By.css('.child'));
403-
expect(child.nativeElement).toBeDefined();
404-
});
405-
});
403+
const child = fixture.debugElement.query(By.css('.child'));
404+
expect(child.nativeElement).toBeDefined();
405+
}));
406406
});
407407

408408
/**
@@ -436,7 +436,7 @@ describe('MatTabGroup', () => {
436436

437437

438438
describe('nested MatTabGroup with enabled animations', () => {
439-
beforeEach(async(() => {
439+
beforeEach(fakeAsync(() => {
440440
TestBed.configureTestingModule({
441441
imports: [MatTabsModule, BrowserAnimationsModule],
442442
declarations: [NestedTabs]
@@ -445,10 +445,11 @@ describe('nested MatTabGroup with enabled animations', () => {
445445
TestBed.compileComponents();
446446
}));
447447

448-
it('should not throw when creating a component with nested tab groups', async(() => {
448+
it('should not throw when creating a component with nested tab groups', fakeAsync(() => {
449449
expect(() => {
450450
let fixture = TestBed.createComponent(NestedTabs);
451451
fixture.detectChanges();
452+
tick();
452453
}).not.toThrow();
453454
}));
454455
});
@@ -594,7 +595,7 @@ class AsyncTabsTestApp {
594595
ngOnInit() {
595596
// Use ngOnInit because there is some issue with scheduling the async task in the constructor.
596597
this.tabs = Observable.create((observer: any) => {
597-
requestAnimationFrame(() => observer.next(this._tabs));
598+
setTimeout(() => observer.next(this._tabs));
598599
});
599600
}
600601
}

0 commit comments

Comments
 (0)