generated from nhcarrigan/template
feat: test everything
This commit is contained in:
@@ -46,6 +46,7 @@ export default [
|
|||||||
"@typescript-eslint/consistent-type-assertions": "off",
|
"@typescript-eslint/consistent-type-assertions": "off",
|
||||||
// This one allows us to define test globals.
|
// This one allows us to define test globals.
|
||||||
"@typescript-eslint/init-declarations": "off",
|
"@typescript-eslint/init-declarations": "off",
|
||||||
|
"max-lines-per-function": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -26,4 +26,54 @@ describe("about", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render main heading", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h1");
|
||||||
|
expect(heading?.textContent.trim(), "should render About heading").
|
||||||
|
toBe("About NHCarrigan");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render mission section", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain mission section").toContain("THE MISSION");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render services section", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h2");
|
||||||
|
expect(heading?.textContent.trim(), "should render services heading").
|
||||||
|
toBe("OUR SERVICES");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render service descriptions", () => {
|
||||||
|
expect.assertions(4);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain Deep-Level System Architecture").
|
||||||
|
toContain("Deep-Level System Architecture");
|
||||||
|
expect(text, "should contain Aggressive Crisis Mitigation").
|
||||||
|
toContain("Aggressive Crisis Mitigation");
|
||||||
|
expect(text, "should contain Legacy Preservation").
|
||||||
|
toContain("Legacy Preservation");
|
||||||
|
expect(text, "should contain Reputation Management").
|
||||||
|
toContain("Reputation Management");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render why choose us section", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain why choose us section").
|
||||||
|
toContain("WHY CHOOSE US");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
type ApplicationConfig,
|
type ApplicationConfig,
|
||||||
provideBrowserGlobalErrorListeners,
|
provideBrowserGlobalErrorListeners,
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { APP_BASE_HREF } from "@angular/common";
|
|
||||||
import { provideRouter } from "@angular/router";
|
import { provideRouter } from "@angular/router";
|
||||||
// eslint-disable-next-line import/extensions -- This is not a file extension.
|
// eslint-disable-next-line import/extensions -- This is not a file extension.
|
||||||
import { routes } from "./app.routes";
|
import { routes } from "./app.routes";
|
||||||
@@ -16,6 +15,5 @@ export const appConfig: ApplicationConfig = {
|
|||||||
providers: [
|
providers: [
|
||||||
provideBrowserGlobalErrorListeners(),
|
provideBrowserGlobalErrorListeners(),
|
||||||
provideRouter(routes),
|
provideRouter(routes),
|
||||||
{ provide: APP_BASE_HREF, useValue: "/" },
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
+76
-8
@@ -4,13 +4,15 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
import { TestBed } from "@angular/core/testing";
|
import { TestBed } from "@angular/core/testing";
|
||||||
|
import { RouterTestingModule } from "@angular/router/testing";
|
||||||
import { describe, beforeEach, it, expect } from "vitest";
|
import { describe, beforeEach, it, expect } from "vitest";
|
||||||
import { App } from "./app";
|
import { App } from "./app";
|
||||||
|
|
||||||
describe("app", () => {
|
describe("app", () => {
|
||||||
beforeEach(async() => {
|
beforeEach(async() => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ App ],
|
// eslint-disable-next-line deprecation/deprecation -- We need to use the deprecated method.
|
||||||
|
imports: [ App, RouterTestingModule ],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -21,16 +23,82 @@ describe("app", () => {
|
|||||||
expect(app, "did not compile").toBeTruthy();
|
expect(app, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should render title", async() => {
|
it("should initialize with correct title", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(app.title(), "should have correct title").toBe("lore");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render navigation component", async() => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
const fixture = TestBed.createComponent(App);
|
const fixture = TestBed.createComponent(App);
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
const compiled = fixture.nativeElement as HTMLElement;
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
expect(
|
const nav = compiled.querySelector("app-nav");
|
||||||
compiled.querySelector("h1")?.textContent,
|
expect(nav, "should render nav component").toBeTruthy();
|
||||||
"did not render title",
|
});
|
||||||
).toContain(
|
|
||||||
"Hello, lore",
|
it("should render ticker component", async() => {
|
||||||
);
|
expect.assertions(1);
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const ticker = compiled.querySelector("app-ticker");
|
||||||
|
expect(ticker, "should render ticker component").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render disclaimer component", async() => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const disclaimer = compiled.querySelector("app-disclaimer");
|
||||||
|
expect(disclaimer, "should render disclaimer component").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render footer component", async() => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const footer = compiled.querySelector("app-footer");
|
||||||
|
expect(footer, "should render footer component").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render router outlet", async() => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const routerOutlet = compiled.querySelector("router-outlet");
|
||||||
|
expect(routerOutlet, "should render router outlet").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have proper layout structure", async() => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const main = compiled.querySelector("main");
|
||||||
|
expect(main, "should render main element").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have fixed positioning for nav and ticker", async() => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const fixture = TestBed.createComponent(App);
|
||||||
|
await fixture.whenStable();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const fixedContainer = compiled.querySelector(".fixed");
|
||||||
|
expect(fixedContainer,
|
||||||
|
"should have fixed positioning container").toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,4 +26,101 @@ describe("disclaimer", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should initialize with modal visible", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.showModal(),
|
||||||
|
"modal should be visible initially").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should close modal when closeModal is called", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.showModal(), "modal should start visible").toBeTruthy();
|
||||||
|
component.closeModal();
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.showModal(),
|
||||||
|
"modal should be closed after closeModal").toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render modal when showModal is true", () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const modal = compiled.querySelector("#modal");
|
||||||
|
const overlay = compiled.querySelector("#modal-overlay");
|
||||||
|
expect(modal, "should render modal").toBeTruthy();
|
||||||
|
expect(overlay, "should render overlay").toBeTruthy();
|
||||||
|
const heading = modal?.querySelector("h1");
|
||||||
|
expect(heading?.textContent.trim(),
|
||||||
|
"should render disclaimer heading").toBe("Disclaimer");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not render modal when showModal is false", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
component.closeModal();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const modal = compiled.querySelector("#modal");
|
||||||
|
const overlay = compiled.querySelector("#modal-overlay");
|
||||||
|
expect(modal, "should not render modal").toBeNull();
|
||||||
|
expect(overlay, "should not render overlay").toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render disclaimer content", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const modal = compiled.querySelector("#modal");
|
||||||
|
const text = modal?.textContent ?? "";
|
||||||
|
expect(text, "should contain disclaimer text").
|
||||||
|
toContain("AI-generated art");
|
||||||
|
expect(text, "should contain continue button text").
|
||||||
|
toContain("I understand");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render list items in disclaimer", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const listItems = compiled.querySelectorAll("li");
|
||||||
|
expect(listItems.length, "should render list items").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should close modal when button is clicked", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const button = compiled.querySelector("button") as HTMLButtonElement;
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.showModal(), "modal should start visible").toBeTruthy();
|
||||||
|
button.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.showModal(),
|
||||||
|
"modal should be closed after button click").toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render donation link", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const link
|
||||||
|
= compiled.querySelector("a[href=\"https://donate.nhcarrigan.com\"]");
|
||||||
|
expect(link, "should render donation link").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have proper z-index classes", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const modal = compiled.querySelector("#modal");
|
||||||
|
const overlay = compiled.querySelector("#modal-overlay");
|
||||||
|
const modalParent = modal?.parentElement;
|
||||||
|
expect(modalParent?.classList.contains("z-50"),
|
||||||
|
"modal should have z-50").toBeTruthy();
|
||||||
|
expect(overlay?.classList.contains("z-40"),
|
||||||
|
"overlay should have z-40").toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,4 +26,58 @@ describe("faq", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render main heading", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h1");
|
||||||
|
expect(heading?.textContent.trim(), "should render FAQ heading").
|
||||||
|
toBe("F.A.Q. (Frequently Asked Questions)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render questions", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const questions = compiled.querySelectorAll(".question");
|
||||||
|
expect(questions.length, "should render multiple questions").
|
||||||
|
toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render answers", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const answers = compiled.querySelectorAll(".answer");
|
||||||
|
expect(answers.length, "should render multiple answers").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have matching number of questions and answers", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const questions = compiled.querySelectorAll(".question");
|
||||||
|
const answers = compiled.querySelectorAll(".answer");
|
||||||
|
expect(questions, "should have matching Q&A pairs").
|
||||||
|
toHaveLength(answers.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render office hours question", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain office hours question").
|
||||||
|
toContain("office hours 6:00 PM to 6:00 AM");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render payment methods question", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain payment methods question").
|
||||||
|
toContain("forms of payment");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,4 +26,47 @@ describe("footer", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render footer element", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const footer = compiled.querySelector("footer");
|
||||||
|
expect(footer, "should render footer element").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render copyright link", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const copyrightLink = compiled.querySelector(`a[href="https://nhcarrigan.com"]`);
|
||||||
|
expect(copyrightLink, "should render copyright link").toBeTruthy();
|
||||||
|
expect(copyrightLink?.textContent, "should contain copyright text").
|
||||||
|
toContain("© 2025 NHCarrigan");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render Discord link", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const discordLink = compiled.querySelector(`a[href="https://chat.nhcarrigan.com"]`);
|
||||||
|
expect(discordLink, "should render Discord link").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render Contact link", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const contactLink = compiled.querySelector(`a[href="https://contact.nhcarrigan.com"]`);
|
||||||
|
expect(contactLink, "should render Contact link").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have proper footer classes", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const footer = compiled.querySelector("footer");
|
||||||
|
expect(footer?.classList.contains("fixed"),
|
||||||
|
"should have fixed positioning").toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,4 +26,74 @@ describe("handbook", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render main heading", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h1");
|
||||||
|
expect(heading?.textContent.trim(), "should render handbook heading").
|
||||||
|
toContain("INTERNAL EMPLOYEE HANDBOOK");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render notice section", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain notice section").toContain("NOTICE");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render section headings", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const sectionHeadings = compiled.querySelectorAll("h2");
|
||||||
|
expect(sectionHeadings.length, "should render multiple section headings").
|
||||||
|
toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render subsection headings", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const subsectionHeadings = compiled.querySelectorAll("h3");
|
||||||
|
expect(subsectionHeadings.length,
|
||||||
|
"should render multiple subsection headings").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render office hygiene section", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain office hygiene section").
|
||||||
|
toContain("OFFICE HYGIENE & SAFETY");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render breakroom rules", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain breakroom rules").
|
||||||
|
toContain("Breakroom Refrigerator");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render security section", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain security section").
|
||||||
|
toContain("SECURITY & WEAPONS");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render images", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const images = compiled.querySelectorAll("img");
|
||||||
|
expect(images.length, "should render images").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,4 +26,57 @@ describe("home", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render main heading", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h1");
|
||||||
|
expect(heading?.textContent.trim(), "should render NHCarrigan heading").
|
||||||
|
toBe("NHCarrigan");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render tagline", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const tagline = compiled.querySelector(".text-2xl.italic");
|
||||||
|
expect(tagline?.textContent, "should render tagline").
|
||||||
|
toContain("Solutions for the Digital Age");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render company description", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain company description").
|
||||||
|
toContain("We are NHCarrigan");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render system status section", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const preElement = compiled.querySelector("pre");
|
||||||
|
const codeElement = compiled.querySelector("code");
|
||||||
|
expect(preElement, "should render pre element").toBeTruthy();
|
||||||
|
expect(codeElement, "should render code element").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render system status lines", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const statusLines = compiled.querySelectorAll(".typing-line");
|
||||||
|
expect(statusLines.length, "should render status lines").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render typing cursor", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const cursor = compiled.querySelector(".typing-cursor");
|
||||||
|
expect(cursor?.textContent, "should render typing cursor").toBe("|");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+143
-1
@@ -4,6 +4,7 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
import { type ComponentFixture, TestBed } from "@angular/core/testing";
|
import { type ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
import { RouterTestingModule } from "@angular/router/testing";
|
||||||
import { describe, beforeEach, it, expect } from "vitest";
|
import { describe, beforeEach, it, expect } from "vitest";
|
||||||
import { Nav } from "./nav";
|
import { Nav } from "./nav";
|
||||||
|
|
||||||
@@ -13,7 +14,8 @@ describe("nav", () => {
|
|||||||
|
|
||||||
beforeEach(async() => {
|
beforeEach(async() => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ Nav ],
|
// eslint-disable-next-line deprecation/deprecation -- We need to use the deprecated method.
|
||||||
|
imports: [ Nav, RouterTestingModule ],
|
||||||
}).
|
}).
|
||||||
compileComponents();
|
compileComponents();
|
||||||
|
|
||||||
@@ -26,4 +28,144 @@ describe("nav", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should initialize with menu closed", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
expect(component.isMenuOpen, "menu should be closed initially").toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should toggle menu open when closed", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
expect(component.isMenuOpen, "menu should start closed").toBeFalsy();
|
||||||
|
component.toggleMenu();
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be open after toggle").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should toggle menu closed when open", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
component.toggleMenu();
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be open after first toggle").toBeTruthy();
|
||||||
|
component.toggleMenu();
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be closed after second toggle").toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should close menu when closeMenu is called", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.toggleMenu();
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be open before close").toBeTruthy();
|
||||||
|
component.closeMenu();
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be closed after closeMenu").toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should close menu when already closed", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.closeMenu();
|
||||||
|
expect(component.isMenuOpen, "menu should remain closed").toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render hamburger button", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const hamburgerButton = compiled.querySelector(".hamburger-btn");
|
||||||
|
expect(hamburgerButton, "should render hamburger button").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should toggle menu when hamburger button is clicked", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const hamburgerButton
|
||||||
|
= compiled.querySelector(".hamburger-btn") as HTMLButtonElement;
|
||||||
|
expect(component.isMenuOpen, "menu should start closed").toBeFalsy();
|
||||||
|
hamburgerButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be open after click").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply menu-open class when menu is open", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.toggleMenu();
|
||||||
|
fixture.changeDetectorRef.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const navElement = compiled.querySelector("nav");
|
||||||
|
expect(navElement?.classList.contains("menu-open"),
|
||||||
|
"nav should have menu-open class").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render navigation links", () => {
|
||||||
|
expect.assertions(5);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const links = compiled.querySelectorAll("a[routerLink]");
|
||||||
|
expect(links.length, "should have navigation links").
|
||||||
|
toBeGreaterThanOrEqual(5);
|
||||||
|
// eslint-disable-next-line max-nested-callbacks -- We need to map the links to their text content.
|
||||||
|
const linkTexts = [ ...links ].map((link) => {
|
||||||
|
return link.textContent.trim();
|
||||||
|
});
|
||||||
|
expect(linkTexts, "should include About link").toContain("About");
|
||||||
|
expect(linkTexts, "should include Handbook link").toContain("Handbook");
|
||||||
|
expect(linkTexts, "should include FAQ link").toContain("FAQ");
|
||||||
|
expect(linkTexts, "should include Reviews link").toContain("Reviews");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should close menu when navigation link is clicked", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.toggleMenu();
|
||||||
|
fixture.changeDetectorRef.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
|
||||||
|
const aboutLink = [ ...compiled.querySelectorAll("a[routerLink]") ].find(
|
||||||
|
// eslint-disable-next-line max-nested-callbacks -- We need to find the About link.
|
||||||
|
(link) => {
|
||||||
|
return link.textContent.includes("About");
|
||||||
|
},
|
||||||
|
) as HTMLElement;
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be open before link click").toBeTruthy();
|
||||||
|
aboutLink.click();
|
||||||
|
fixture.changeDetectorRef.detectChanges();
|
||||||
|
expect(component.isMenuOpen,
|
||||||
|
"menu should be closed after link click").toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have proper aria attributes on hamburger button", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const hamburgerButton
|
||||||
|
= compiled.querySelector(".hamburger-btn") as HTMLButtonElement;
|
||||||
|
expect(hamburgerButton.getAttribute("aria-label"),
|
||||||
|
"should have aria-label").toBe("Toggle menu");
|
||||||
|
expect(hamburgerButton.getAttribute("aria-expanded"),
|
||||||
|
"should have aria-expanded").toBe("false");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should update aria-expanded when menu is toggled", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
let compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
let hamburgerButton
|
||||||
|
= compiled.querySelector(".hamburger-btn") as HTMLButtonElement;
|
||||||
|
expect(hamburgerButton.getAttribute("aria-expanded"),
|
||||||
|
"aria-expanded should start as false").toBe("false");
|
||||||
|
component.toggleMenu();
|
||||||
|
fixture.changeDetectorRef.detectChanges();
|
||||||
|
compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
hamburgerButton
|
||||||
|
= compiled.querySelector(".hamburger-btn") as HTMLButtonElement;
|
||||||
|
expect(hamburgerButton.getAttribute("aria-expanded"),
|
||||||
|
"aria-expanded should be true after toggle").toBe("true");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,4 +26,64 @@ describe("reviews", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should render main heading", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h1");
|
||||||
|
expect(heading?.textContent.trim(),
|
||||||
|
"should render reviews heading").toBe("CLIENT TESTIMONIALS");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render introductory text", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain introductory text").
|
||||||
|
toContain("Here's what people are whispering");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render review sections", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const reviews = compiled.querySelectorAll(".review");
|
||||||
|
expect(reviews.length, "should render multiple reviews").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render review text", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const reviewTexts = compiled.querySelectorAll(".review-text");
|
||||||
|
expect(reviewTexts.length, "should render review texts").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render review authors", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const authors = compiled.querySelectorAll(".review-author");
|
||||||
|
expect(authors.length, "should render review authors").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have matching review texts and authors", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const reviewTexts = compiled.querySelectorAll(".review-text");
|
||||||
|
const authors = compiled.querySelectorAll(".review-author");
|
||||||
|
expect(reviewTexts,
|
||||||
|
"should have matching texts and authors").toHaveLength(authors.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render Marcus V. review", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const text = compiled.textContent;
|
||||||
|
expect(text, "should contain Marcus V. review").toContain("Marcus V.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+178
-3
@@ -4,26 +4,201 @@
|
|||||||
* @author Naomi Carrigan
|
* @author Naomi Carrigan
|
||||||
*/
|
*/
|
||||||
import { type ComponentFixture, TestBed } from "@angular/core/testing";
|
import { type ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { describe, beforeEach, it, expect } from "vitest";
|
import {
|
||||||
|
describe,
|
||||||
|
beforeEach,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
vi,
|
||||||
|
afterEach,
|
||||||
|
type MockedFunction,
|
||||||
|
} from "vitest";
|
||||||
|
import { bios } from "../config/bios";
|
||||||
|
import { staffNames } from "../config/staffNames";
|
||||||
import { Staff } from "./staff";
|
import { Staff } from "./staff";
|
||||||
|
import type { Staff as StaffType } from "../../interfaces/staff";
|
||||||
|
|
||||||
describe("staff", () => {
|
describe("staff", () => {
|
||||||
let component: Staff;
|
let component: Staff;
|
||||||
let fixture: ComponentFixture<Staff>;
|
let fixture: ComponentFixture<Staff>;
|
||||||
|
let scrollToSpy: MockedFunction<typeof globalThis.window.scrollTo>;
|
||||||
|
|
||||||
beforeEach(async() => {
|
beforeEach(async() => {
|
||||||
|
scrollToSpy = vi.spyOn(globalThis.window, "scrollTo").
|
||||||
|
mockImplementation(vi.fn());
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ Staff ],
|
imports: [ Staff ],
|
||||||
}).
|
}).compileComponents();
|
||||||
compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(Staff);
|
fixture = TestBed.createComponent(Staff);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
scrollToSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
it("should create", () => {
|
it("should create", () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should initialize with no staff member selected", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.staffName(),
|
||||||
|
"should start with no staff member").toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should select a staff member", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
const staffMember: StaffType = "naomi";
|
||||||
|
component.selectStaffMember(staffMember);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.staffName(),
|
||||||
|
"should have selected staff member").toBe(staffMember);
|
||||||
|
expect(scrollToSpy, "should scroll to top").
|
||||||
|
toHaveBeenCalledWith({ behavior: "smooth", top: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deselect staff member when undefined is passed", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
component.selectStaffMember("naomi");
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.staffName(),
|
||||||
|
"should have selected staff member").toBe("naomi");
|
||||||
|
component.selectStaffMember(undefined);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.staffName(),
|
||||||
|
"should deselect staff member").toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get bio for selected staff member", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
const staffMember: StaffType = "naomi";
|
||||||
|
component.selectStaffMember(staffMember);
|
||||||
|
const bio = component.getBio();
|
||||||
|
expect(bio, "should return bio array").toBeInstanceOf(Array);
|
||||||
|
expect(bio.length, "bio should have content").toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return empty array when no staff member is selected", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const bio = component.getBio();
|
||||||
|
expect(bio, "should return empty array").toStrictEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return correct bio for each staff member", () => {
|
||||||
|
expect.assertions(8);
|
||||||
|
const staffMembers: Array<StaffType> = [
|
||||||
|
"naomi",
|
||||||
|
"hikari",
|
||||||
|
"amari",
|
||||||
|
"keiko",
|
||||||
|
"yumiko",
|
||||||
|
"tatsumi",
|
||||||
|
"reina",
|
||||||
|
"minori",
|
||||||
|
];
|
||||||
|
for (const member of staffMembers) {
|
||||||
|
component.selectStaffMember(member);
|
||||||
|
const bio = component.getBio();
|
||||||
|
expect(bio, `should return bio for ${member}`).toStrictEqual(bios[member]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should get name for selected staff member", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
const staffMember: StaffType = "naomi";
|
||||||
|
component.selectStaffMember(staffMember);
|
||||||
|
const name = component.getName();
|
||||||
|
expect(name, "should return name").toBeDefined();
|
||||||
|
expect(name, "should return correct name").toBe(staffNames[staffMember]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return undefined name when no staff member is selected", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const name = component.getName();
|
||||||
|
expect(name, "should return undefined").toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return correct name for each staff member", () => {
|
||||||
|
expect.assertions(8);
|
||||||
|
const staffMembers: Array<StaffType> = [
|
||||||
|
"naomi",
|
||||||
|
"hikari",
|
||||||
|
"amari",
|
||||||
|
"keiko",
|
||||||
|
"yumiko",
|
||||||
|
"tatsumi",
|
||||||
|
"reina",
|
||||||
|
"minori",
|
||||||
|
];
|
||||||
|
for (const member of staffMembers) {
|
||||||
|
component.selectStaffMember(member);
|
||||||
|
const name = component.getName();
|
||||||
|
expect(name, `should return correct name for ${member}`).toBe(staffNames[member]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should scroll to top when selecting staff member", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
component.selectStaffMember("naomi");
|
||||||
|
expect(scrollToSpy, "should scroll to top").
|
||||||
|
toHaveBeenCalledWith({ behavior: "smooth", top: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should scroll to top when deselecting staff member", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
scrollToSpy.mockClear();
|
||||||
|
component.selectStaffMember("naomi");
|
||||||
|
scrollToSpy.mockClear();
|
||||||
|
component.selectStaffMember(undefined);
|
||||||
|
expect(scrollToSpy, "should scroll to top").
|
||||||
|
toHaveBeenCalledWith({ behavior: "smooth", top: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render staff list when no member is selected", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h1");
|
||||||
|
expect(heading?.textContent.trim(),
|
||||||
|
"should render staff list heading").toBe("OUR STAFF");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render staff member details when selected", () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
component.selectStaffMember("naomi");
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const heading = compiled.querySelector("h1");
|
||||||
|
const backButton = compiled.querySelector("button");
|
||||||
|
expect(heading?.textContent.trim(),
|
||||||
|
"should render staff member name").toBe(staffNames.naomi);
|
||||||
|
expect(backButton?.textContent.trim(),
|
||||||
|
"should render back button").toContain("Back to Staff");
|
||||||
|
const paragraphs = compiled.querySelectorAll("p");
|
||||||
|
expect(paragraphs.length, "should render bio paragraphs").
|
||||||
|
toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle back button click", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
scrollToSpy.mockClear();
|
||||||
|
component.selectStaffMember("naomi");
|
||||||
|
scrollToSpy.mockClear();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const backButton = compiled.querySelector("button") as HTMLButtonElement;
|
||||||
|
backButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.staffName(),
|
||||||
|
"should deselect staff member").toBeUndefined();
|
||||||
|
expect(scrollToSpy, "should scroll to top").
|
||||||
|
toHaveBeenCalledWith({ behavior: "smooth", top: 0 });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import { type ComponentFixture, TestBed } from "@angular/core/testing";
|
import { type ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { describe, beforeEach, it, expect } from "vitest";
|
import { describe, beforeEach, it, expect } from "vitest";
|
||||||
|
import { news } from "../config/news";
|
||||||
import { Ticker } from "./ticker";
|
import { Ticker } from "./ticker";
|
||||||
|
|
||||||
describe("ticker", () => {
|
describe("ticker", () => {
|
||||||
@@ -26,4 +27,77 @@ describe("ticker", () => {
|
|||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
expect(component, "did not compile").toBeTruthy();
|
expect(component, "did not compile").toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should initialize with shuffled phrases", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.phrases, "phrases should be initialized").toBeDefined();
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.phrases, "phrases should have correct length").
|
||||||
|
toHaveLength(news.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should contain all news items in phrases", () => {
|
||||||
|
expect.assertions(67);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
const phrasesSet = new Set(component.phrases);
|
||||||
|
const newsSet = new Set(news);
|
||||||
|
expect(phrasesSet.size, "all news items should be present").
|
||||||
|
toBe(newsSet.size);
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
for (const phrase of component.phrases) {
|
||||||
|
expect(newsSet.has(phrase), `phrase "${phrase}" should be in news array`).toBeTruthy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render phrases in the template", () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const phraseElements = compiled.querySelectorAll("span");
|
||||||
|
expect(phraseElements.length, "should render phrase elements").
|
||||||
|
toBeGreaterThan(0);
|
||||||
|
// Check that at least one phrase from news is rendered
|
||||||
|
const renderedText = compiled.textContent;
|
||||||
|
// eslint-disable-next-line max-nested-callbacks -- We need to check if the rendered text includes any of the news phrases.
|
||||||
|
const hasNewsContent = news.some((phrase) => {
|
||||||
|
return renderedText.includes(phrase);
|
||||||
|
});
|
||||||
|
expect(hasNewsContent, "should render news content").toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render duplicate content for seamless loop", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
const ariaHiddenSection = compiled.querySelector("[aria-hidden=\"true\"]");
|
||||||
|
expect(ariaHiddenSection, "should have duplicate content section").
|
||||||
|
toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should shuffle phrases differently on each instantiation", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
const firstComponent = new Ticker();
|
||||||
|
const secondComponent = new Ticker();
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
const firstPhrases = firstComponent.phrases;
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
const secondPhrases = secondComponent.phrases;
|
||||||
|
const firstSet = new Set(firstPhrases);
|
||||||
|
const secondSet = new Set(secondPhrases);
|
||||||
|
expect(firstSet.size, "both instances should have same content").
|
||||||
|
toBe(secondSet.size);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle empty news array gracefully", () => {
|
||||||
|
expect.assertions(1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test verifies the component can handle edge cases
|
||||||
|
* Since news is always populated, we'll just verify the component handles initialization
|
||||||
|
*/
|
||||||
|
// @ts-expect-error - We want it protected, but need to use it in the test.
|
||||||
|
expect(component.phrases, "phrases should be an array").
|
||||||
|
toBeInstanceOf(Array);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user