Files
Portfolio/tests/unit/section-controller.test.ts

174 lines
5.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
activationLineDocumentY,
deriveSectionActivationSnapshot,
deriveSectionState,
isAtDocumentEnd,
selectActiveSection,
type SectionHeadingGeometry,
} from "@/journey/section-controller";
const headings: readonly SectionHeadingGeometry[] = [
{ id: "start", top: 100 },
{ id: "profile", top: 350 },
{ id: "experience", top: 700 },
{ id: "infrastructure", top: 1100 },
{ id: "selected-builds", top: 1500 },
{ id: "additional-work", top: 1900 },
{ id: "toolkit", top: 2300 },
{ id: "contact", top: 2700 },
];
describe("active section selection", () => {
it("places the activation line at a literal 35 percent of the visual viewport", () => {
expect(
activationLineDocumentY({
scrollY: 200,
visualViewportOffsetTop: 25,
visualViewportHeight: 1000,
}),
).toBe(575);
});
it("forces document end within exactly two CSS pixels using current geometry", () => {
expect(
isAtDocumentEnd({
scrollY: 498,
scrollHeight: 1000,
visualViewportHeight: 500,
}),
).toBe(true);
expect(
isAtDocumentEnd({
scrollY: 497.999,
scrollHeight: 1000,
visualViewportHeight: 500,
}),
).toBe(false);
expect(
isAtDocumentEnd({
scrollY: 498,
scrollHeight: 1200,
visualViewportHeight: 500,
}),
).toBe(false);
});
it("keeps the first section before any heading reaches the activation line", () => {
expect(
selectActiveSection({ headings, activationY: 99, atDocumentEnd: false }),
).toBe("start");
});
it("selects the last heading at or above the literal 35 percent activation line", () => {
expect(
selectActiveSection({
headings,
activationY: 699.999,
atDocumentEnd: false,
}),
).toBe("profile");
expect(
selectActiveSection({ headings, activationY: 700, atDocumentEnd: false }),
).toBe("experience");
});
it("lets later document order win when heading positions are identical", () => {
expect(
selectActiveSection({
headings: [
{ id: "start", top: 0 },
{ id: "profile", top: 350 },
{ id: "experience", top: 350 },
],
activationY: 350,
atDocumentEnd: false,
}),
).toBe("experience");
});
it("forces the final canonical section at document end", () => {
expect(
selectActiveSection({ headings, activationY: 1400, atDocumentEnd: true }),
).toBe("contact");
});
it("uses fresh late-growth geometry on every selection", () => {
const grown = headings.map((heading) =>
heading.id === "experience" ? { ...heading, top: 900 } : heading,
);
expect(
selectActiveSection({ headings, activationY: 750, atDocumentEnd: false }),
).toBe("experience");
expect(
selectActiveSection({
headings: grown,
activationY: 750,
atDocumentEnd: false,
}),
).toBe("profile");
});
it("derives responsive heading offsets from the same activation-line geometry", () => {
expect(deriveSectionActivationSnapshot({
headings: [
{ id: "start", top: 100 },
{ id: "profile", top: 420 },
{ id: "experience", top: 1_120 },
],
activeSectionId: "profile",
activationY: 500,
viewportHeight: 800,
layoutRevision: 7,
})).toEqual({
layoutRevision: 7,
activeHeadingOffsetVh: -0.1,
nextHeadingOffsetVh: 0.775,
});
});
it("returns no following-heading offset for the final active heading", () => {
expect(deriveSectionActivationSnapshot({
headings: [
{ id: "start", top: 100 },
{ id: "profile", top: 420 },
],
activeSectionId: "profile",
activationY: 500,
viewportHeight: 800,
layoutRevision: 8,
})).toEqual({
layoutRevision: 8,
activeHeadingOffsetVh: -0.1,
nextHeadingOffsetVh: null,
});
});
it("maps every canonical section to its chapter and arrival state", () => {
expect([
deriveSectionState("start"),
deriveSectionState("profile"),
deriveSectionState("experience"),
deriveSectionState("infrastructure"),
deriveSectionState("selected-builds"),
deriveSectionState("additional-work"),
deriveSectionState("toolkit"),
deriveSectionState("contact"),
]).toEqual([
{ activeSectionId: "start", chapter: "deep-night", arrived: false },
{ activeSectionId: "profile", chapter: "deep-night", arrived: false },
{ activeSectionId: "experience", chapter: "systems", arrived: false },
{
activeSectionId: "infrastructure",
chapter: "infrastructure",
arrived: false,
},
{ activeSectionId: "selected-builds", chapter: "builds", arrived: false },
{ activeSectionId: "additional-work", chapter: "builds", arrived: false },
{ activeSectionId: "toolkit", chapter: "pre-dawn", arrived: false },
{ activeSectionId: "contact", chapter: "destination", arrived: true },
]);
});
});