test.web/presentation/components/link.component.test.ts
test.web/presentation/components/link.component.test.ts is a file in Codebase Testing. 85 lines of code and 0 definitions.
import "@banes-lab/web/presentation/records/grammar.record.ts";
import {
createExternalLink,
createLink,
createPageLink,
} from "@banes-lab/web/presentation/components/link.component.ts";
import { describe, expect, it, vi } from "vitest";
import { pagePath, tabLink } from "@banes-lab/web/core/assets/link.assets.ts";
import { GRAMMAR_PAGE } from "@banes-lab/web/core/ids/page.ids.ts";
import { MAIN_ID } from "@banes-lab/web/core/ids/site.ids.ts";
import { ROUTE_REQUESTED } from "@banes-lab/web/core/ids/route.ids.ts";
import { getPage } from "@banes-lab/web/domain/registries/page.registry.ts";
import { subscribeEvent } from "@banes-lab/web/core/buses/base.bus.ts";
const CLASS_NAME = "link";
const LABEL = "Open";
const EXTERNAL = "https://example.com/";
const TAB = "guide";
const GENERIC = "Start";
const CONTEXT_SELECTOR = ".visually-hidden";
const requestedPages = function requestedPages(link: HTMLAnchorElement): readonly string[] {
const pages: string[] = [];
const dispose = subscribeEvent(ROUTE_REQUESTED, (event) => {
pages.push(event.page + (event.path ?? ""));
});
link.click();
dispose();
return pages;
};
describe("createPageLink", () => {
it("points at the page path and requests the route on click", () => {
const link = createPageLink(GRAMMAR_PAGE, CLASS_NAME, [LABEL]);
expect(link.pathname).toBe(pagePath(GRAMMAR_PAGE));
expect(link.className).toBe(CLASS_NAME);
expect(requestedPages(link)).toStrictEqual([GRAMMAR_PAGE + pagePath(GRAMMAR_PAGE)]);
});
it("gives a generic label its destination page as hidden context, and leaves a descriptive one alone", () => {
const generic = createPageLink(GRAMMAR_PAGE, CLASS_NAME, [GENERIC]);
expect(generic.querySelector(CONTEXT_SELECTOR)?.textContent).toContain(getPage(GRAMMAR_PAGE)?.title);
expect(createPageLink(GRAMMAR_PAGE, CLASS_NAME, [LABEL]).querySelector(CONTEXT_SELECTOR)).toBeNull();
});
});
describe("createExternalLink", () => {
it("opens in a new tab with a safe rel", () => {
const link = createExternalLink(EXTERNAL, CLASS_NAME, [LABEL]);
expect(link.href).toBe(EXTERNAL);
expect(link.target).toBe("_blank");
expect(link.rel.includes("noopener")).toBe(true);
});
});
describe("createLink", () => {
it("routes internal hrefs through the page link, keeping the full path", () => {
const href = tabLink(GRAMMAR_PAGE, TAB, "setup");
const link = createLink(href, CLASS_NAME, [LABEL]);
expect(link.getAttribute("href")).toBe(href);
expect(requestedPages(link)).toStrictEqual([GRAMMAR_PAGE + href]);
expect(createLink(`${pagePath(GRAMMAR_PAGE)}?x=1`, CLASS_NAME, [LABEL]).getAttribute("href")).toBe(
`${pagePath(GRAMMAR_PAGE)}?x=1`,
);
});
it("treats other hrefs as external", () => {
expect(createLink(EXTERNAL, CLASS_NAME, [LABEL]).target).toBe("_blank");
});
it("keeps a bare anchor on the page, reveals its collapsed target and leaves the class off when none is given", () => {
const main = document.createElement("main");
main.id = MAIN_ID;
Object.defineProperty(main, "scrollTo", { configurable: true, value: vi.fn() });
const target = document.createElement("article");
target.id = "record-one";
const details = document.createElement("details");
target.append(details);
main.append(target);
document.body.append(main);
const link = createLink("#record-one", "", [LABEL]);
expect(link.getAttribute("href")).toBe("#record-one");
expect(link.hasAttribute("class")).toBe(false);
expect(requestedPages(link)).toStrictEqual([]);
expect(details.open).toBe(true);
main.remove();
});
it("scrolls instead of re-routing when a full path points at the tab already shown", () => {
const href = `${window.location.pathname}#elsewhere`;
const link = createLink(href, CLASS_NAME, [LABEL]);
expect(requestedPages(link)).toStrictEqual([]);
});
});