Testing
@data-xplorer/core/headless drives the explorer without a canvas. Nothing in it imports
PixiJS, and it needs no DOM, so it runs in bun test, Vitest or Node’s test runner.
createTestStack(source, options?) loads the first scene and returns a stack that navigates
the way the explorer does, with every move un-animated. This file is not an illustration: it runs
in this repository’s CI on every change.
// Runs in this repository's CI with `bun test`; Vitest and Node's test runner work the same way.import { expect, test } from "bun:test";import { createSceneSource } from "@data-xplorer/core";import { createTestStack } from "@data-xplorer/core/headless";
const folders: Record<string, string[]> = { "/": ["/src", "/docs"], "/src": ["/src/lib"], "/src/lib": [], "/docs": [] };
const source = createSceneSource<string>({ load: (focus) => ({ id: focus ?? "/", heading: focus ?? "my-app", clusters: folders[focus ?? "/"]!.map((path) => ({ id: path, label: path, weight: 1, href: `#${path}`, drillsInto: path, shape: "cloud" as const, })), nodes: [], edges: [], }), parentOf: (path) => { const parent = path.slice(0, path.lastIndexOf("/")) || "/"; return { kind: "anchored", focus: parent === "/" ? null : parent, anchorId: path }; },});
test("a folder opens, and zooming out comes back", async () => { const explorer = await createTestStack(source); // no canvas, no animation, no DOM expect(explorer.current.heading).toBe("my-app");
await explorer.enter("/src"); // an item's id in the current scene, as a double-click would expect(explorer.current.focus).toBe("/src"); expect(explorer.breadcrumb()).toEqual(["my-app", "/src"]);
await explorer.zoomOut(); // back the way it came expect(explorer.current.focus).toBeNull();});
test("a deep link starts deep, and zooming out follows parentOf", async () => { const explorer = await createTestStack(source, { focus: "/src/lib" }); await explorer.zoomOut(); expect(explorer.current.focus).toBe("/src");});
test("every item is laid out inside the scene", async () => { const explorer = await createTestStack(source); for (const node of explorer.current.renderNodes) expect(node.size).toBeGreaterThan(0);});| Member | Does |
|---|---|
current |
The scene the viewer is in: focus, heading, the scene your source returned, and renderNodes (each item as laid out, with x, y and size). |
depth |
How many scenes deep the viewer is from where they started. |
enter(itemId) |
Enters that item. A non-drillable item, or a failed load, leaves the stack where it was. |
zoomOut() |
Leaves the way it came in; at the starting scene, follows your parentOf. |
enterSibling(itemId), jumpTo(focus) |
Moves sideways, or jumps as a deep link would. |
breadcrumb() |
The headings from the start to here. |
stack, rig |
The underlying scene stack and camera, for anything else. |
./headless also exports resolveOptions (every option with its default), parseColour,
appearanceFor (the colour and shape the canvas picks for an item) and the stack and camera the
test stack is built from.