The component we test against:
import {Component, OnInit} from '@angular/core';
import {Course} from "../model/course";
import {Observable} from "rxjs";
import {CoursesService} from "../services/courses.service";
import {map} from "rxjs/operators";
import {sortCoursesBySeqNo} from './sort-course-by-seq';
@Component({
selector: 'home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
beginnerCourses$: Observable<Course[]>;
advancedCourses$: Observable<Course[]>;
constructor(private coursesService: CoursesService) {
}
ngOnInit() {
this.reloadCourses();
}
reloadCourses() {
const courses$ = this.coursesService.findAllCourses();
this.beginnerCourses$ = this.filterByCategory(courses$, 'BEGINNER');
this.advancedCourses$ = this.filterByCategory(courses$, 'ADVANCED');
}
filterByCategory(courses$: Observable<Course[]>, category:string) {
return courses$.pipe(
map(courses => courses.filter(course => course.category === category).sort(sortCoursesBySeqNo) )
);
}
}
Template:
<div class="container"> <h3>All Courses</h3> <mat-tab-group> <ng-container *ngIf="beginnerCourses$ | async as beginnerCourses"> <mat-tab label="Beginners" *ngIf="beginnerCourses?.length > 0"> <courses-card-list (courseEdited)="reloadCourses()" [courses]="beginnerCourses" > </courses-card-list> </mat-tab> </ng-container> <ng-container *ngIf="advancedCourses$ | async as advancedCourses"> <mat-tab label="Advanced" *ngIf="advancedCourses?.length > 0"> <courses-card-list (courseEdited)="reloadCourses()" [courses]="advancedCourses" > </courses-card-list> </mat-tab> </ng-container> </mat-tab-group> </div>
Testing code:
import { async, ComponentFixture, fakeAsync, flush, flushMicrotasks, TestBed, tick } from "@angular/core/testing"; import { CoursesModule } from "../courses.module"; import { DebugElement } from "@angular/core"; import { HomeComponent } from "./home.component"; import { CoursesService } from "../services/courses.service"; import { setupCourses } from "../common/setup-test-data"; import { By } from "@angular/platform-browser"; import { of } from "rxjs"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; import { click, textContent } from "../common/test-utils"; describe("HomeComponent", () => { let fixture: ComponentFixture<HomeComponent>, component: HomeComponent, el: DebugElement, coursesService: any; // TestBed.inject(CoursesService) will fix type issue const beginnerCourses = setupCourses().filter( cs => cs.category === "BEGINNER" ); const advancedCourses = setupCourses().filter( cs => cs.category === "ADVANCED" ); beforeEach(async(() => { const coursesServiceSpy = jasmine.createSpyObj("CoursesService", [ "findAllCourses" ]); TestBed.configureTestingModule({ imports: [CoursesModule, NoopAnimationsModule], providers: [{ provide: CoursesService, useValue: coursesServiceSpy }] }) .compileComponents() .then(() => { fixture = TestBed.createComponent(HomeComponent); component = fixture.componentInstance; el = fixture.debugElement; coursesService = TestBed.get(CoursesService); }); })); it("should create the component", () => { expect(component).toBeTruthy(); }); it("should display only beginner courses", () => { coursesService.findAllCourses.and.returnValue(of(beginnerCourses)); fixture.detectChanges(); // only one tab available const tabs = el.queryAll(By.css(".mat-tab-label")); expect(tabs.length).toEqual(1, "Unexpect number of tabs"); }); it("should display only advanced courses", () => { coursesService.findAllCourses.and.returnValue(of(advancedCourses)); fixture.detectChanges(); const tabs = el.queryAll(By.css(".mat-tab-label")); expect(tabs.length).toEqual(1, "Unexpect number of tabs"); }); it("should display both tabs", () => { coursesService.findAllCourses.and.returnValue(of(setupCourses())); fixture.detectChanges(); const tabs = el.queryAll(By.css(".mat-tab-label")); expect(tabs.length).toEqual(2, "Unexpect number of tabs"); }); /** * * async vs fakeAsync * * Depends on whether you need to call real http, if yes, then async * Otherwise, always fakeAsync */ it("should display advanced courses when tab clicked - fakeAsync", fakeAsync(() => { coursesService.findAllCourses.and.returnValue(of(setupCourses())); fixture.detectChanges(); const tabs = el.queryAll(By.css(".mat-tab-label")); click(tabs[1]); fixture.detectChanges(); flush(); const cardTitles = el.queryAll(By.css(".mat-card-title")); expect(cardTitles.length).toBeGreaterThan(0, "Could not find card titles"); expect(textContent(cardTitles[0])).toContain("Angular Security Course"); })); it("should display advanced courses when tab clicked - async", async(() => { coursesService.findAllCourses.and.returnValue(of(setupCourses())); fixture.detectChanges(); const tabs = el.queryAll(By.css(".mat-tab-label")); click(tabs[1]); fixture.detectChanges(); // async will tigger whenStable callback fixture.whenStable().then(() => { const cardTitles = el.queryAll(By.css(".mat-card-title")); expect(cardTitles.length).toBeGreaterThan( 0, "Could not find card titles" ); expect(textContent(cardTitles[0])).toContain("Angular Security Course"); }); })); });