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.
This commit is contained in:
Kristiyan Kostadinov
2018-03-06 00:08:54 +01:00
committed by Jeremy Elbourn
parent a943b36c7b
commit 8228c5f1fd
2 changed files with 74 additions and 75 deletions
+2 -4
View File
@@ -77,10 +77,8 @@ export class MatTabBodyPortal extends CdkPortalOutlet implements OnInit, OnDestr
this.attach(this._host._content);
}
this._centeringSub = this._host._beforeCentering.subscribe((isCentering: boolean) => {
if (isCentering) {
if (!this.hasAttached()) {
this.attach(this._host._content);
}
if (isCentering && !this.hasAttached()) {
this.attach(this._host._content);
}
});
+72 -71
View File
@@ -8,7 +8,7 @@ import {MatTab, MatTabGroup, MatTabHeaderPosition, MatTabsModule} from './index'
describe('MatTabGroup', () => {
beforeEach(async(() => {
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
imports: [MatTabsModule, NoopAnimationsModule],
declarations: [
@@ -59,7 +59,7 @@ describe('MatTabGroup', () => {
checkSelectedIndex(2, fixture);
});
it('should support two-way binding for selectedIndex', async(() => {
it('should support two-way binding for selectedIndex', fakeAsync(() => {
let component = fixture.componentInstance;
component.selectedIndex = 0;
@@ -67,13 +67,13 @@ describe('MatTabGroup', () => {
let tabLabel = fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1];
tabLabel.nativeElement.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.selectedIndex).toBe(1);
});
tick();
expect(component.selectedIndex).toBe(1);
}));
// Note: needs to be `async` in order to fail when we expect it to.
it('should set to correct tab on fast change', async(() => {
let component = fixture.componentInstance;
component.selectedIndex = 0;
@@ -255,74 +255,76 @@ describe('MatTabGroup', () => {
describe('dynamic binding tabs', () => {
let fixture: ComponentFixture<SimpleDynamicTabsTestApp>;
beforeEach(async(() => {
beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(SimpleDynamicTabsTestApp);
fixture.detectChanges();
tick();
fixture.detectChanges();
}));
it('should be able to add a new tab, select it, and have correct origin position', () => {
fixture.detectChanges();
const component: MatTabGroup =
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
it('should be able to add a new tab, select it, and have correct origin position',
fakeAsync(() => {
const component: MatTabGroup =
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
let tabs: MatTab[] = component._tabs.toArray();
expect(tabs[0].origin).toBe(null);
expect(tabs[1].origin).toBe(0);
expect(tabs[2].origin).toBe(null);
let tabs: MatTab[] = component._tabs.toArray();
expect(tabs[0].origin).toBe(null);
expect(tabs[1].origin).toBe(0);
expect(tabs[2].origin).toBe(null);
// Add a new tab on the right and select it, expect an origin >= than 0 (animate right)
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to right of index'});
fixture.componentInstance.selectedIndex = 4;
fixture.detectChanges();
// Add a new tab on the right and select it, expect an origin >= than 0 (animate right)
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to right of index'});
fixture.componentInstance.selectedIndex = 4;
fixture.detectChanges();
tick();
tabs = component._tabs.toArray();
expect(tabs[3].origin).toBeGreaterThanOrEqual(0);
tabs = component._tabs.toArray();
expect(tabs[3].origin).toBeGreaterThanOrEqual(0);
// Add a new tab in the beginning and select it, expect an origin < than 0 (animate left)
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'});
fixture.componentInstance.selectedIndex = 0;
fixture.detectChanges();
// Add a new tab in the beginning and select it, expect an origin < than 0 (animate left)
fixture.componentInstance.tabs.push({label: 'New tab', content: 'to left of index'});
fixture.componentInstance.selectedIndex = 0;
fixture.detectChanges();
tick();
tabs = component._tabs.toArray();
expect(tabs[0].origin).toBeLessThan(0);
});
tabs = component._tabs.toArray();
expect(tabs[0].origin).toBeLessThan(0);
}));
it('should update selected index if the last tab removed while selected', () => {
fixture.detectChanges();
it('should update selected index if the last tab removed while selected', fakeAsync(() => {
const component: MatTabGroup =
fixture.debugElement.query(By.css('mat-tab-group')).componentInstance;
const numberOfTabs = component._tabs.length;
fixture.componentInstance.selectedIndex = numberOfTabs - 1;
fixture.detectChanges();
tick();
// Remove last tab while last tab is selected, expect next tab over to be selected
fixture.componentInstance.tabs.pop();
fixture.detectChanges();
tick();
expect(component.selectedIndex).toBe(numberOfTabs - 2);
});
}));
});
describe('async tabs', () => {
let fixture: ComponentFixture<AsyncTabsTestApp>;
it('should show tabs when they are available', async(() => {
it('should show tabs when they are available', fakeAsync(() => {
fixture = TestBed.createComponent(AsyncTabsTestApp);
let labels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
expect(labels.length).toBe(0);
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(0);
fixture.detectChanges();
tick();
fixture.detectChanges();
tick();
fixture.whenStable().then(() => {
fixture.detectChanges();
labels = fixture.debugElement.queryAll(By.css('.mat-tab-label'));
expect(labels.length).toBe(2);
});
expect(fixture.debugElement.queryAll(By.css('.mat-tab-label')).length).toBe(2);
}));
});
@@ -330,50 +332,48 @@ describe('MatTabGroup', () => {
let fixture: ComponentFixture<TabGroupWithSimpleApi>;
let tabGroup: MatTabGroup;
beforeEach(() => {
beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(TabGroupWithSimpleApi);
fixture.detectChanges();
tick();
tabGroup =
fixture.debugElement.query(By.directive(MatTabGroup)).componentInstance as MatTabGroup;
});
}));
it('should support a tab-group with the simple api', async(() => {
it('should support a tab-group with the simple api', fakeAsync(() => {
expect(getSelectedLabel(fixture).textContent).toMatch('Junk food');
expect(getSelectedContent(fixture).textContent).toMatch('Pizza, fries');
tabGroup.selectedIndex = 2;
fixture.detectChanges();
// Use whenStable to wait for async observables and change detection to run in content.
fixture.whenStable().then(() => {
tick();
expect(getSelectedLabel(fixture).textContent).toMatch('Fruit');
expect(getSelectedContent(fixture).textContent).toMatch('Apples, grapes');
expect(getSelectedLabel(fixture).textContent).toMatch('Fruit');
expect(getSelectedContent(fixture).textContent).toMatch('Apples, grapes');
fixture.componentInstance.otherLabel = 'Chips';
fixture.componentInstance.otherContent = 'Salt, vinegar';
fixture.detectChanges();
fixture.componentInstance.otherLabel = 'Chips';
fixture.componentInstance.otherContent = 'Salt, vinegar';
fixture.detectChanges();
expect(getSelectedLabel(fixture).textContent).toMatch('Chips');
expect(getSelectedContent(fixture).textContent).toMatch('Salt, vinegar');
});
expect(getSelectedLabel(fixture).textContent).toMatch('Chips');
expect(getSelectedContent(fixture).textContent).toMatch('Salt, vinegar');
}));
it('should support @ViewChild in the tab content', () => {
expect(fixture.componentInstance.legumes).toBeTruthy();
});
it('should only have the active tab in the DOM', async(() => {
it('should only have the active tab in the DOM', fakeAsync(() => {
expect(fixture.nativeElement.textContent).toContain('Pizza, fries');
expect(fixture.nativeElement.textContent).not.toContain('Peanuts');
tabGroup.selectedIndex = 3;
fixture.detectChanges();
// Use whenStable to wait for async observables and change detection to run in content.
fixture.whenStable().then(() => {
expect(fixture.nativeElement.textContent).not.toContain('Pizza, fries');
expect(fixture.nativeElement.textContent).toContain('Peanuts');
});
tick();
expect(fixture.nativeElement.textContent).not.toContain('Pizza, fries');
expect(fixture.nativeElement.textContent).toContain('Peanuts');
}));
it('should support setting the header position', () => {
@@ -389,20 +389,20 @@ describe('MatTabGroup', () => {
});
describe('lazy loaded tabs', () => {
it('should lazy load the second tab', async () => {
let fixture = TestBed.createComponent(TemplateTabs);
it('should lazy load the second tab', fakeAsync(() => {
const fixture = TestBed.createComponent(TemplateTabs);
fixture.detectChanges();
tick();
let secondLabel = fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1];
const secondLabel = fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1];
secondLabel.nativeElement.click();
fixture.detectChanges();
tick();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let child = fixture.debugElement.query(By.css('.child'));
expect(child.nativeElement).toBeDefined();
});
});
const child = fixture.debugElement.query(By.css('.child'));
expect(child.nativeElement).toBeDefined();
}));
});
/**
@@ -436,7 +436,7 @@ describe('MatTabGroup', () => {
describe('nested MatTabGroup with enabled animations', () => {
beforeEach(async(() => {
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
imports: [MatTabsModule, BrowserAnimationsModule],
declarations: [NestedTabs]
@@ -445,10 +445,11 @@ describe('nested MatTabGroup with enabled animations', () => {
TestBed.compileComponents();
}));
it('should not throw when creating a component with nested tab groups', async(() => {
it('should not throw when creating a component with nested tab groups', fakeAsync(() => {
expect(() => {
let fixture = TestBed.createComponent(NestedTabs);
fixture.detectChanges();
tick();
}).not.toThrow();
}));
});
@@ -594,7 +595,7 @@ class AsyncTabsTestApp {
ngOnInit() {
// Use ngOnInit because there is some issue with scheduling the async task in the constructor.
this.tabs = Observable.create((observer: any) => {
requestAnimationFrame(() => observer.next(this._tabs));
setTimeout(() => observer.next(this._tabs));
});
}
}