Skip to content

Commit

Permalink
Model description (#415)
Browse files Browse the repository at this point in the history
* Added truncation to project description cards

* Added descriptionHtml to models

* Added description input explanation

* Fix card component unit tests

* Fix home component unit tests

* Fixed remaining tests

* Fix description styling

* Added stripHtml tests

Forced all fake models to use a Required version of their interface. This will help track down any changes to the model without associated updates to the generator.

* Updated model type definitions

* Simplified fake model generators

* Removed stripHtml

Co-authored-by: Anthony Truskinger <[email protected]>
  • Loading branch information
Allcharles and atruskie authored Sep 2, 2020
1 parent 7a51095 commit 5b52392
Show file tree
Hide file tree
Showing 58 changed files with 575 additions and 494 deletions.
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

0 comments on commit 5b52392

Please sign in to comment.