Skip to content

Commit 5adb8d6

Browse files
committed
Finish specs for max code coverage
1 parent 5080e66 commit 5adb8d6

File tree

6 files changed

+223
-11
lines changed

6 files changed

+223
-11
lines changed

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"server": "ts-node -P ./server/server.tsconfig.json ./server/server.ts",
99
"build": "ng build",
1010
"test": "ng test",
11+
"test:coverage": "ng test --code-coverage",
1112
"lint": "ng lint",
1213
"cypress:open": "cypress open",
1314
"cypress:run": "cypress run",
@@ -59,6 +60,7 @@
5960
"karma-coverage-istanbul-reporter": "~3.0.2",
6061
"karma-jasmine": "~4.0.0",
6162
"karma-jasmine-html-reporter": "^1.5.0",
63+
"ng-mocks": "^11.1.1",
6264
"protractor": "~7.0.0",
6365
"start-server-and-test": "^1.9.1",
6466
"ts-node": "~3.2.0",
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { DebugElement } from "@angular/core";
2+
import { ComponentFixture, TestBed } from "@angular/core/testing";
3+
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
4+
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
5+
import { MockProvider } from "ng-mocks";
6+
import { of } from "rxjs";
7+
import { COURSES } from "../../../../server/db-data";
8+
import { CoursesModule } from "../courses.module";
9+
import { Course } from "../model/course";
10+
import { CoursesService } from "../services/courses.service";
11+
import { CourseDialogComponent } from "./course-dialog.component";
12+
13+
describe("CourseDialogComponent", function () {
14+
15+
let fixture: ComponentFixture<CourseDialogComponent>;
16+
let component: CourseDialogComponent;
17+
let el: DebugElement;
18+
19+
let coursesService: SpyObject<Pick<CoursesService, "saveCourse">>;
20+
let matDialogRef: SpyObject<MatDialogRef<Course>>;
21+
22+
beforeEach(async function () {
23+
coursesService = jasmine.createSpyObj("CoursesService", ["saveCourse"]);
24+
coursesService.saveCourse.and.callFake((courseId, changes) => of({
25+
...COURSES[courseId],
26+
...changes,
27+
}));
28+
matDialogRef = jasmine.createSpyObj("MatDialogRef", ["close"])
29+
30+
TestBed.configureTestingModule({
31+
imports: [CoursesModule, NoopAnimationsModule],
32+
declarations: [CourseDialogComponent],
33+
providers: [
34+
MockProvider(CoursesService, coursesService),
35+
MockProvider(MatDialogRef, matDialogRef),
36+
{
37+
provide: MAT_DIALOG_DATA,
38+
useValue: COURSES["12"],
39+
},
40+
]
41+
}).compileComponents().then(() => {
42+
fixture = TestBed.createComponent(CourseDialogComponent);
43+
component = fixture.componentInstance;
44+
el = fixture.debugElement;
45+
});
46+
});
47+
48+
it("is created", function () {
49+
expect(component).toBeTruthy();
50+
});
51+
52+
it("is initialized", function () {
53+
fixture.detectChanges();
54+
expect(component).toBeTruthy();
55+
});
56+
57+
58+
it("closes the dialog", function () {
59+
fixture.detectChanges();
60+
component.close();
61+
expect(matDialogRef.close).toHaveBeenCalledTimes(1);
62+
});
63+
64+
it("saves the course and closes the dialog", function () {
65+
fixture.detectChanges();
66+
component.save();
67+
68+
expect(coursesService.saveCourse).toHaveBeenCalled();
69+
expect(matDialogRef.close).toHaveBeenCalledTimes(1);
70+
});
71+
});
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { DebugElement } from "@angular/core";
2+
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from "@angular/core/testing";
3+
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
4+
import { ActivatedRoute } from "@angular/router";
5+
import { RouterTestingModule } from "@angular/router/testing";
6+
import { MockProvider } from "ng-mocks";
7+
import { of } from "rxjs";
8+
import { COURSES, findLessonsForCourse } from "../../../../server/db-data";
9+
import { CoursesModule } from "../courses.module";
10+
import { Lesson } from "../model/lesson";
11+
import { CoursesService } from "../services/courses.service";
12+
import { CourseComponent } from "./course.component";
13+
14+
describe("CourseComponent", function () {
15+
16+
let component: CourseComponent;
17+
let fixture: ComponentFixture<CourseComponent>;
18+
let el: DebugElement;
19+
let coursesService: SpyObject<Pick<CoursesService, "findLessons">>;
20+
21+
beforeEach(waitForAsync(function () {
22+
coursesService = jasmine.createSpyObj("CoursesService", ["findLessons"]);
23+
coursesService.findLessons.and.callFake((courseId) => of(findLessonsForCourse(courseId)));
24+
25+
TestBed.configureTestingModule({
26+
imports: [
27+
CoursesModule,
28+
RouterTestingModule,
29+
NoopAnimationsModule,
30+
],
31+
providers: [
32+
MockProvider(CoursesService, coursesService),
33+
{
34+
provide: ActivatedRoute,
35+
useValue: {
36+
snapshot: {
37+
data: {
38+
course: COURSES["12"],
39+
}
40+
}
41+
}
42+
}
43+
],
44+
declarations: [CourseComponent]
45+
}).compileComponents().then(() => {
46+
fixture = TestBed.createComponent(CourseComponent);
47+
component = fixture.componentInstance;
48+
el = fixture.debugElement;
49+
});
50+
}));
51+
52+
it("is created", function () {
53+
expect(component).toBeTruthy();
54+
});
55+
56+
it("is initialized", async function () {
57+
fixture.detectChanges();
58+
59+
let lessons: Lesson[];
60+
component.dataSource.connect({} as any).subscribe(data => {
61+
lessons = data;
62+
});
63+
64+
expect(lessons).toEqual(findLessonsForCourse(12), "Unexpected list of lessons");
65+
});
66+
67+
it("resets page index on sort change", function () {
68+
fixture.detectChanges();
69+
70+
component.paginator.nextPage();
71+
expect(component.paginator.pageIndex).toBe(1);
72+
73+
component.sort.sort({
74+
id: "description",
75+
start: "asc",
76+
disableClear: false,
77+
});
78+
79+
expect(component.paginator.pageIndex).toBe(0);
80+
});
81+
82+
83+
it("resets page index on search", fakeAsync(function () {
84+
fixture.detectChanges();
85+
86+
let lessons: Lesson[];
87+
component.dataSource.connect({} as any).subscribe(data => {
88+
lessons = data;
89+
});
90+
91+
component.paginator.nextPage();
92+
expect(component.paginator.pageIndex).toBe(1);
93+
94+
const input: HTMLInputElement = component.input.nativeElement;
95+
input.value = "testing";
96+
input.dispatchEvent(new Event("keyup"));
97+
// tick(149);
98+
tick(150);
99+
100+
expect(component.paginator.pageIndex).toBe(0);
101+
expect(coursesService.findLessons).toHaveBeenCalledWith(12, "testing", "asc", 0 , 3);
102+
}));
103+
});

src/app/courses/courses-card-list/courses-card-list.component.spec.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
2-
import { CoursesCardListComponent } from './courses-card-list.component';
3-
import { CoursesModule } from '../courses.module';
4-
import { COURSES } from '../../../../server/db-data';
51
import { DebugElement } from '@angular/core';
2+
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
3+
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
64
import { By } from '@angular/platform-browser';
7-
import { sortCoursesBySeqNo } from '../home/sort-course-by-seq';
8-
import { Course } from '../model/course';
5+
import { of } from 'rxjs';
96
import { setupCourses } from '../common/setup-test-data';
7+
import { CoursesCardListComponent } from './courses-card-list.component';
108

119

1210

@@ -16,11 +14,17 @@ describe("CoursesCardListComponent", () => {
1614
let component: CoursesCardListComponent;
1715
let fixture: ComponentFixture<CoursesCardListComponent>;
1816
let el: DebugElement;
17+
let matDialog = jasmine.createSpyObj("MatDialog", ["open"]) as SpyObject<MatDialog>;
1918

2019
beforeEach(waitForAsync(function () {
2120
TestBed.configureTestingModule({
22-
imports: [CoursesModule],
23-
declarations: []
21+
providers: [
22+
{
23+
provide: MatDialog,
24+
useValue: matDialog,
25+
}
26+
],
27+
declarations: [CoursesCardListComponent]
2428
}).compileComponents().then(() => {
2529

2630
fixture = TestBed.createComponent(CoursesCardListComponent);
@@ -71,6 +75,32 @@ describe("CoursesCardListComponent", () => {
7175
expect(title.nativeElement.textContent).toBe(course.titles.description);
7276
expect(image.nativeElement.src).toBe(course.iconUrl);
7377
});
78+
79+
describe("#editCourse", function () {
80+
81+
it("opens a dialog", () => {
82+
// *** This spec does not require `fakeAsync` ***
83+
// This spec is completely synchronous, as it mocks the `afterClosed`
84+
// method of `DialogRef` and returns a synchronous observable by using `of`.
85+
86+
matDialog.open.and.returnValue({
87+
afterClosed() {
88+
return of(component.courses[1]);
89+
}
90+
} as MatDialogRef<any>);
91+
92+
component.courses = setupCourses();
93+
fixture.detectChanges();
94+
95+
let eventCalled = false;
96+
component.courseEdited.subscribe(() => {
97+
eventCalled = true;
98+
});
99+
100+
component.editCourse(component.courses[0]);
101+
expect(eventCalled).toBeTrue();
102+
});
103+
});
74104
});
75105

76106

src/app/courses/home/home.component.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ describe('HomeComponent', () => {
102102
});
103103

104104

105-
fit("should display advanced courses when tab clicked -- fakeAsync", fakeAsync(() => {
105+
it("should display advanced courses when tab clicked -- fakeAsync", fakeAsync(() => {
106106
// `of` creates an observable that **immediately** emits its value. All of this is done
107107
// synchronously, the emission and the completion. This is, this spec can be completely
108108
// synchronous!!!
@@ -121,7 +121,7 @@ describe('HomeComponent', () => {
121121
expect(cardTitles[0].nativeElement.textContent).toContain("Angular Security Course");
122122
}));
123123

124-
fit("should display advanced courses when tab clicked -- waitForAsync", waitForAsync(() => {
124+
it("should display advanced courses when tab clicked -- waitForAsync", waitForAsync(() => {
125125
// `of` creates an observable that **immediately** emits its value. All of this is done
126126
// synchronously, the emission and the completion. This is, this spec can be completely
127127
// synchronous!!!
@@ -142,7 +142,7 @@ describe('HomeComponent', () => {
142142
});
143143
}));
144144

145-
fit("should display advanced courses when tab clicked -- Typescript async", async () => {
145+
it("should display advanced courses when tab clicked -- Typescript async", async () => {
146146
// `of` creates an observable that **immediately** emits its value. All of this is done
147147
// synchronously, the emission and the completion. This is, this spec can be completely
148148
// synchronous!!!

0 commit comments

Comments
 (0)