101 lines
3.0 KiB
TypeScript
101 lines
3.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { nextSign } from "@/journey/sign-controller";
|
|
import type { SignSnapshot } from "@/journey/types";
|
|
|
|
const passiveSample = (
|
|
distanceVh: number,
|
|
overrides: Partial<Parameters<typeof nextSign>[1]> = {},
|
|
): Parameters<typeof nextSign>[1] => ({
|
|
source: "passive-scroll",
|
|
deltaY: 10,
|
|
viewportHeight: 100,
|
|
crossedBoundaries: 0,
|
|
candidateId: "profile",
|
|
distanceVh,
|
|
motionAllowed: true,
|
|
...overrides,
|
|
});
|
|
|
|
describe("forward-only sign latching", () => {
|
|
it("latches a first eligible candidate at the exact approach boundary", () => {
|
|
expect(nextSign(null, passiveSample(1.2))).toEqual({
|
|
targetId: "profile",
|
|
phase: "approach",
|
|
progress: 0,
|
|
});
|
|
});
|
|
|
|
it("maps the literal approach and pass ranges independently", () => {
|
|
expect(nextSign(null, passiveSample(0.875))).toEqual({
|
|
targetId: "profile",
|
|
phase: "approach",
|
|
progress: expect.closeTo(0.5, 14),
|
|
});
|
|
expect(nextSign(null, passiveSample(-0.05))).toEqual({
|
|
targetId: "profile",
|
|
phase: "pass",
|
|
progress: 0.5,
|
|
});
|
|
});
|
|
|
|
it("uses exact stabilized, pass, and completion boundaries", () => {
|
|
const latched: SignSnapshot = {
|
|
targetId: "profile",
|
|
phase: "approach",
|
|
progress: 0,
|
|
};
|
|
|
|
expect(nextSign(latched, passiveSample(0.55))).toEqual({
|
|
targetId: "profile",
|
|
phase: "stabilized",
|
|
progress: 1,
|
|
});
|
|
expect(nextSign(latched, passiveSample(0.15))).toEqual({
|
|
targetId: "profile",
|
|
phase: "pass",
|
|
progress: 0,
|
|
});
|
|
expect(nextSign(latched, passiveSample(-0.25))).toBeNull();
|
|
});
|
|
|
|
it("retains the one latched target while the semantic section advances", () => {
|
|
const latched: SignSnapshot = {
|
|
targetId: "profile",
|
|
phase: "stabilized",
|
|
progress: 1,
|
|
};
|
|
|
|
expect(
|
|
nextSign(latched, passiveSample(0.1, { candidateId: "experience" })),
|
|
).toEqual({
|
|
targetId: "profile",
|
|
phase: "pass",
|
|
progress: expect.closeTo(0.125, 14),
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
["reverse input", { deltaY: -2 }],
|
|
["zero-direction input", { deltaY: 0 }],
|
|
["explicit navigation", { source: "explicit-navigation" as const }],
|
|
["history restoration", { source: "history-restoration" as const }],
|
|
["initial positioning", { source: "initial" as const }],
|
|
["more than one activation boundary", { crossedBoundaries: 2 }],
|
|
["a jump over 0.75 viewport heights", { deltaY: 76 }],
|
|
["disabled motion", { motionAllowed: false }],
|
|
])("clears or suppresses signs for %s", (_label, overrides) => {
|
|
const latched: SignSnapshot = {
|
|
targetId: "profile",
|
|
phase: "approach",
|
|
progress: 0.5,
|
|
};
|
|
expect(nextSign(latched, passiveSample(0.5, overrides))).toBeNull();
|
|
});
|
|
|
|
it("does not latch before visibility or after a jump directly past completion", () => {
|
|
expect(nextSign(null, passiveSample(1.20001))).toBeNull();
|
|
expect(nextSign(null, passiveSample(-0.25))).toBeNull();
|
|
expect(nextSign(null, passiveSample(-1))).toBeNull();
|
|
});
|
|
});
|