feat: persist presentation figure focus
- Add focusPath to MainLocation and FigureBeatDefinition to SceneBeatDefinition - Canonical hash format: #scene/<scene>/<beat>/focus/<segment>/<segment> - Add set_focus_path reducer action - Next/previous restores destination beat's canonical Focus Path - Discussion return preserves exact originating Focus Path - Fix catalog cycle detection to cover disconnected subgraphs - Add FigureCatalogIssue typed issue model - Fix test fixtures using nonexistent edge endpoints - Remove unsafe non-null assertions in catalog and focus modules
This commit is contained in:
@@ -9,7 +9,7 @@ import { defaultMainLocation, type MainLocation } from "./storyboard.js";
|
||||
|
||||
describe("storyboard navigation", () => {
|
||||
it("round-trips main and discussion hashes", () => {
|
||||
const main: MainLocation = { kind: "main", sceneId: "lifecycle", beatId: "deployment" };
|
||||
const main: MainLocation = { kind: "main", sceneId: "lifecycle", beatId: "deployment", focusPath: [] };
|
||||
expect(locationFromHash(hashForLocation(main))).toEqual(main);
|
||||
expect(locationFromHash("#discuss/hosted-automation")).toEqual({
|
||||
kind: "discussion",
|
||||
@@ -28,24 +28,47 @@ describe("storyboard navigation", () => {
|
||||
expect(locationFromHash("#discuss/%ZZ")).toEqual(defaultMainLocation);
|
||||
});
|
||||
|
||||
it("round-trips a recursive Focus Path", () => {
|
||||
const location: MainLocation = {
|
||||
kind: "main",
|
||||
sceneId: "architecture",
|
||||
beatId: "runtime",
|
||||
focusPath: ["runtime-providers", "configured-providers"],
|
||||
};
|
||||
expect(hashForLocation(location)).toBe(
|
||||
"#scene/architecture/runtime/focus/runtime-providers/configured-providers",
|
||||
);
|
||||
expect(locationFromHash(hashForLocation(location))).toEqual(location);
|
||||
});
|
||||
|
||||
it("decodes escaped focus segments and rejects malformed encoding", () => {
|
||||
expect(locationFromHash("#scene/architecture/runtime/focus/runtime%20providers"))
|
||||
.toMatchObject({ focusPath: ["runtime providers"] });
|
||||
expect(locationFromHash("#scene/architecture/runtime/focus/%ZZ"))
|
||||
.toEqual(defaultMainLocation);
|
||||
});
|
||||
|
||||
it("advances within a scene before advancing to the next scene", () => {
|
||||
expect(nextMainLocation({ kind: "main", sceneId: "thesis", beatId: "title" })).toEqual({
|
||||
expect(nextMainLocation({ kind: "main", sceneId: "thesis", beatId: "title", focusPath: [] })).toEqual({
|
||||
kind: "main",
|
||||
sceneId: "thesis",
|
||||
beatId: "substrate",
|
||||
focusPath: [],
|
||||
});
|
||||
expect(nextMainLocation({ kind: "main", sceneId: "thesis", beatId: "substrate" })).toEqual({
|
||||
expect(nextMainLocation({ kind: "main", sceneId: "thesis", beatId: "substrate", focusPath: [] })).toEqual({
|
||||
kind: "main",
|
||||
sceneId: "problem",
|
||||
beatId: "direct-actions",
|
||||
focusPath: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("rewinds across scene boundaries", () => {
|
||||
expect(previousMainLocation({ kind: "main", sceneId: "problem", beatId: "direct-actions" })).toEqual({
|
||||
expect(previousMainLocation({ kind: "main", sceneId: "problem", beatId: "direct-actions", focusPath: [] })).toEqual({
|
||||
kind: "main",
|
||||
sceneId: "thesis",
|
||||
beatId: "substrate",
|
||||
focusPath: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user