127 lines
4.2 KiB
TypeScript
127 lines
4.2 KiB
TypeScript
import { getNotebookFrame } from "./notebookTimeline";
|
|
|
|
describe("getNotebookFrame", () => {
|
|
it("clamps progress below zero and starts with a closed device", () => {
|
|
expect(getNotebookFrame(-1, 4)).toMatchObject({
|
|
phase: "approach",
|
|
lidAngle: 0,
|
|
activeTabIndex: -1,
|
|
cursorVisible: false,
|
|
});
|
|
});
|
|
|
|
it("clamps progress above one and ends with a closed device", () => {
|
|
expect(getNotebookFrame(2, 4)).toMatchObject({
|
|
phase: "close",
|
|
lidAngle: 0,
|
|
activeTabIndex: -1,
|
|
cursorVisible: false,
|
|
});
|
|
});
|
|
|
|
it("keeps the phase boundaries deterministic", () => {
|
|
expect(getNotebookFrame(0.1, 4).phase).toBe("open");
|
|
expect(getNotebookFrame(0.25, 4).phase).toBe("boot");
|
|
expect(getNotebookFrame(0.34, 4).phase).toBe("cursor-in");
|
|
expect(getNotebookFrame(0.44, 4).phase).toBe("browse");
|
|
expect(getNotebookFrame(0.78, 4).phase).toBe("hold");
|
|
expect(getNotebookFrame(0.87, 4).phase).toBe("close");
|
|
});
|
|
|
|
it("opens the lid before the browser starts", () => {
|
|
const frame = getNotebookFrame(0.22, 4);
|
|
|
|
expect(frame.phase).toBe("open");
|
|
expect(frame.lidAngle).toBeGreaterThan(0);
|
|
expect(frame.screenOpacity).toBe(0);
|
|
});
|
|
|
|
it("boots the screen before the cursor enters", () => {
|
|
expect(getNotebookFrame(0.25, 4).screenOpacity).toBe(0);
|
|
expect(getNotebookFrame(0.34, 4).screenOpacity).toBe(1);
|
|
expect(getNotebookFrame(0.4, 4).cursorVisible).toBe(true);
|
|
});
|
|
|
|
it("turns the screen off before the lid closes", () => {
|
|
const screenOff = getNotebookFrame(0.9, 4);
|
|
const closing = getNotebookFrame(0.95, 4);
|
|
|
|
expect(screenOff).toMatchObject({
|
|
phase: "close",
|
|
lidAngle: 112,
|
|
screenOpacity: 0,
|
|
});
|
|
expect(closing.screenOpacity).toBe(0);
|
|
expect(closing.lidAngle).toBeGreaterThan(0);
|
|
expect(closing.lidAngle).toBeLessThan(112);
|
|
});
|
|
|
|
it("selects each tab only inside the browse window", () => {
|
|
expect(getNotebookFrame(0.43, 4).activeTabIndex).toBe(-1);
|
|
expect(getNotebookFrame(0.48, 4).activeTabIndex).toBe(0);
|
|
expect(getNotebookFrame(0.57, 4).activeTabIndex).toBe(1);
|
|
expect(getNotebookFrame(0.67, 4).activeTabIndex).toBe(2);
|
|
expect(getNotebookFrame(0.76, 4).activeTabIndex).toBe(3);
|
|
});
|
|
|
|
it("holds the final tab until the closing phase", () => {
|
|
expect(getNotebookFrame(0.84, 4)).toMatchObject({
|
|
phase: "hold",
|
|
activeTabIndex: 3,
|
|
cursorVisible: true,
|
|
});
|
|
expect(getNotebookFrame(0.88, 4)).toMatchObject({
|
|
phase: "close",
|
|
activeTabIndex: -1,
|
|
cursorVisible: false,
|
|
});
|
|
});
|
|
|
|
it("emits a deterministic cursor click pulse for each tab", () => {
|
|
const first = getNotebookFrame(0.475, 4);
|
|
const later = getNotebookFrame(0.56, 4);
|
|
|
|
expect(first.cursorClicking).toBe(true);
|
|
expect(later.cursorClicking).toBe(true);
|
|
expect(first.cursorX).toBeGreaterThanOrEqual(0);
|
|
expect(first.cursorX).toBeLessThanOrEqual(1);
|
|
expect(first.cursorY).toBeGreaterThanOrEqual(0);
|
|
expect(first.cursorY).toBeLessThanOrEqual(1);
|
|
});
|
|
|
|
it("moves the cursor between tab targets instead of teleporting", () => {
|
|
const previous = getNotebookFrame(0.525, 4);
|
|
const moving = getNotebookFrame(0.532, 4);
|
|
const target = getNotebookFrame(0.56, 4);
|
|
|
|
expect(moving.cursorX).toBeGreaterThan(previous.cursorX);
|
|
expect(moving.cursorX).toBeLessThan(target.cursorX);
|
|
expect(moving.cursorY).toBe(target.cursorY);
|
|
});
|
|
|
|
it("lands the click pulse on the center of the selected tab", () => {
|
|
for (const progress of [0.475, 0.56, 0.65, 0.735]) {
|
|
const frame = getNotebookFrame(progress, 4);
|
|
expect(frame.cursorClicking).toBe(true);
|
|
expect(frame.cursorX).toBeCloseTo((frame.activeTabIndex + 0.5) / 4, 6);
|
|
}
|
|
});
|
|
|
|
it("tracks the real number of tabs", () => {
|
|
expect(getNotebookFrame(0.68, 2).cursorX).toBeCloseTo(0.75, 6);
|
|
expect(getNotebookFrame(0.6, 3).cursorX).toBeCloseTo(0.5, 6);
|
|
});
|
|
|
|
it("is deterministic when rewound to the same progress", () => {
|
|
expect(getNotebookFrame(0.61, 4)).toEqual(getNotebookFrame(0.61, 4));
|
|
});
|
|
|
|
it("does not expose an invalid tab when there are no projects", () => {
|
|
expect(getNotebookFrame(0.6, 0)).toMatchObject({
|
|
activeTabIndex: -1,
|
|
cursorVisible: false,
|
|
cursorClicking: false,
|
|
});
|
|
});
|
|
});
|