Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model description #415

Merged
merged 17 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"just-camel-case": "^4.0.2",
"just-snake-case": "^1.1.0",
"luxon": "^1.25.0",
"ngx-line-truncation": "^1.6.6",
"ngx-toastr": "^13.0.0",
"rxjs": "^6.6.2",
"snazzy-info-window": "^1.1.1",
Expand Down
304 changes: 129 additions & 175 deletions src/app/components/home/home.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,219 +1,173 @@
import {
HttpClientTestingModule,
HttpTestingController,
} from "@angular/common/http/testing";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { RouterTestingModule } from "@angular/router/testing";
import { ApiErrorDetails } from "@baw-api/api.interceptor.service";
import { Filters } from "@baw-api/baw-api.service";
import { MockBawApiModule } from "@baw-api/baw-apiMock.module";
import { ProjectsService } from "@baw-api/project/projects.service";
import { SecurityService } from "@baw-api/security/security.service";
import { Project } from "@models/Project";
import { SpyObject } from "@ngneat/spectator";
import {
createComponentFactory,
Spectator,
SpyObject,
} from "@ngneat/spectator";
import { AppConfigService } from "@services/app-config/app-config.service";
import { cmsRoot } from "@services/app-config/app-config.service.spec";
import { SharedModule } from "@shared/shared.module";
import { CardImageComponent } from "@shared/cards/card-image/card-image.component";
import { CardsComponent } from "@shared/cards/cards.component";
import { CmsComponent } from "@shared/cms/cms.component";
import { generateApiErrorDetails } from "@test/fakes/ApiErrorDetails";
import { generateProject } from "@test/fakes/Project";
import { nStepObservable } from "@test/helpers/general";
import { assertRoute } from "@test/helpers/html";
import { MockComponent } from "ng-mocks";
import { Subject } from "rxjs";
import { HomeComponent } from "./home.component";

describe("HomeComponent", () => {
let httpMock: HttpTestingController;
let projectApi: SpyObject<ProjectsService>;
let securityApi: SecurityService;
let component: HomeComponent;
let env: AppConfigService;
let fixture: ComponentFixture<HomeComponent>;
let cmsUrl: string;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [HomeComponent],
imports: [
SharedModule,
HttpClientTestingModule,
RouterTestingModule,
MockBawApiModule,
],
}).compileComponents();

fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
httpMock = TestBed.inject(HttpTestingController);
projectApi = TestBed.inject(ProjectsService) as SpyObject<ProjectsService>;
securityApi = TestBed.inject(SecurityService);
env = TestBed.inject(AppConfigService);

cmsUrl = `${cmsRoot}/home.html`;
let config: AppConfigService;
let spectator: Spectator<HomeComponent>;
const createComponent = createComponentFactory({
component: HomeComponent,
declarations: [
CardsComponent,
MockComponent(CardImageComponent),
MockComponent(CmsComponent),
],
imports: [RouterTestingModule, MockBawApiModule],
});

afterEach(() => {
httpMock.verify();
});
async function interceptProjects(
projects: Project[] = [],
error?: ApiErrorDetails
) {
const subject = new Subject<Project[]>();
const promise = nStepObservable(
subject,
() => (error ? error : projects),
!projects
);
projectApi.filter.and.callFake(() => subject);
spectator.detectChanges();
await promise;
spectator.detectChanges();
}

function interceptCmsRequest() {
const req = httpMock.expectOne(cmsUrl);
req.flush("<h1>Test Header</h1><p>Test Description</p>");
function getCardImages() {
return spectator.queryAll(CardImageComponent);
}

it("should load cms", async () => {
const subject = new Subject<Project[]>();
const promise = nStepObservable(subject, () => []);
projectApi.filter.and.callFake(() => subject);
function getButton() {
return spectator.query<HTMLButtonElement>("button");
}

await promise;
fixture.detectChanges();
interceptCmsRequest();
fixture.detectChanges();
beforeEach(() => {
spectator = createComponent({ detectChanges: false });

const header = fixture.nativeElement.querySelector("h1");
const body = fixture.nativeElement.querySelector("p");
projectApi = spectator.inject(ProjectsService);
securityApi = spectator.inject(SecurityService);
config = spectator.inject(AppConfigService);
});

expect(header).toBeTruthy();
expect(header.innerText.trim()).toBe("Test Header");
expect(body).toBeTruthy();
expect(body.innerText.trim()).toBe("Test Description");
it("should load cms", async () => {
await interceptProjects();
const cms = spectator.query(CmsComponent);
expect(cms.page).toBe("/home.html");
});

describe("page", () => {
async function setupComponent(
projects: Project[],
error?: ApiErrorDetails
) {
const subject = new Subject<Project[]>();
const promise = nStepObservable(
subject,
() => (projects ? projects : error),
!projects
);
projectApi.filter.and.callFake(() => subject);

fixture.detectChanges();
await promise;
interceptCmsRequest();
fixture.detectChanges();
}

function getCardImages() {
return fixture.nativeElement.querySelectorAll("baw-card-image");
}

function getCardTitle(card: HTMLElement): HTMLElement {
return card.querySelector(".card-title");
}

function getCardText(card: HTMLElement): HTMLElement {
return card.querySelector(".card-text");
}

function getButton() {
return fixture.nativeElement.querySelector("button");
}

it("should create", async () => {
await setupComponent([]);
expect(component).toBeTruthy();
});
it("should create", async () => {
await interceptProjects();
expect(spectator.component).toBeTruthy();
});

it("should request 3 projects", async () => {
await setupComponent([]);
expect(projectApi.filter).toHaveBeenCalledWith({
paging: { items: 3 },
} as Filters);
});
it("should request 3 projects", async () => {
await interceptProjects();
expect(projectApi.filter).toHaveBeenCalledWith({
paging: { items: 3 },
} as Filters);
});

it("should handle filter error", async () => {
await setupComponent(undefined, generateApiErrorDetails());
expect(getCardImages().length).toBe(0);
expect(getButton()).toBeTruthy();
});
it("should handle filter error", async () => {
await interceptProjects(undefined, generateApiErrorDetails());
expect(getCardImages().length).toBe(0);
expect(getButton()).toBeTruthy();
});

it("should display no projects", async () => {
await setupComponent([]);
expect(getCardImages().length).toBe(0);
expect(getButton()).toBeTruthy();
});
it("should display no projects", async () => {
await interceptProjects([]);
expect(getCardImages().length).toBe(0);
expect(getButton()).toBeTruthy();
});

it("should display single project", async () => {
await setupComponent([new Project(generateProject())]);
it("should display single project", async () => {
await interceptProjects([new Project(generateProject())]);
expect(getCardImages().length).toBe(1);
expect(getButton()).toBeTruthy();
});

const cards = getCardImages();
expect(cards.length).toBe(1);
expect(getButton()).toBeTruthy();
});
it("should display project name", async () => {
await interceptProjects([
new Project({ ...generateProject(), name: "Project" }),
]);

it("should display project name", async () => {
await setupComponent([
new Project({ ...generateProject(), name: "Project" }),
]);
const cards = getCardImages();
expect(cards[0].card.title).toBe("Project");
});

const cards = getCardImages();
expect(getCardTitle(cards[0]).innerText.trim()).toBe("Project");
});
it("should display description", async () => {
await interceptProjects([
new Project({
...generateProject(),
descriptionHtmlTagline: "Description",
}),
]);

it("should display description", async () => {
await setupComponent([
new Project({
...generateProject(),
description: "Description",
}),
]);
const cards = getCardImages();
expect(cards[0].card.description).toBe("Description");
});

const cards = getCardImages();
expect(getCardText(cards[0]).innerText.trim()).toBe("Description");
});
it("should display missing description", async () => {
await interceptProjects([
new Project({
...generateProject(),
descriptionHtmlTagline: undefined,
}),
]);

it("should display missing description", async () => {
await setupComponent([
new Project({
...generateProject(),
description: undefined,
}),
]);

const cards = getCardImages();
expect(getCardText(cards[0]).innerText.trim()).toBe(
"No description given"
);
});
const cards = getCardImages();
expect(cards[0].card.description).toBe(undefined);
});

it("should display multiple projects", async () => {
const ids = [1, 2, 3];
const names = ids.map((id) => `Project ${id}`);
const descriptions = ids.map((id) => `Description ${id}`);
await setupComponent(
ids.map(
(id, index) =>
new Project({
...generateProject(id),
name: names[index],
description: descriptions[index],
})
)
);

const cards = getCardImages();
expect(cards.length).toBe(ids.length);
expect(getButton()).toBeTruthy();
ids.forEach((_, index) => {
expect(getCardTitle(cards[index]).innerText.trim()).toBe(names[index]);
expect(getCardText(cards[index]).innerText.trim()).toBe(
descriptions[index]
);
});
it("should display multiple projects", async () => {
const ids = [1, 2, 3];
const names = ids.map((id) => `Project ${id}`);
const descriptions = ids.map((id) => `Description ${id}`);
await interceptProjects(
ids.map(
(id, index) =>
new Project({
...generateProject(id),
name: names[index],
descriptionHtmlTagline: descriptions[index],
})
)
);

const cards = getCardImages();
expect(cards.length).toBe(ids.length);
expect(getButton()).toBeTruthy();
ids.forEach((_, index) => {
expect(cards[index].card.title).toBe(names[index]);
expect(cards[index].card.description).toBe(descriptions[index]);
});
});

it("should link to project details page", async () => {
await setupComponent([]);
it("should link to project details page", async () => {
await interceptProjects([]);

const button = getButton();
expect(button).toBeTruthy();
expect(button.innerText.trim()).toBe("More Projects");
assertRoute(button, "/projects");
});
const button = getButton();
expect(button).toBeTruthy();
expect(button.innerText.trim()).toBe("More Projects");
assertRoute(button, "/projects");
});
});
Loading