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"); }); })); });