import assert from 'node:assert/strict'; import test from 'node:test'; import type { Project, SceneGraphEdge, SceneGraphNode } from '../types'; import { asGraphNodeId, asSceneId } from '../types/ids'; import { getActiveStorylineGraphNodeIds, listActiveStorylineScenes } from './activeStorylineScenes'; function node(id: string, sceneId: string, extra: Partial = {}): SceneGraphNode { return { id: asGraphNodeId(id), sceneId: asSceneId(sceneId), x: 0, y: 0, isStartScene: false, isSideStoryStart: false, sideStoryLineTitle: '', ...extra, }; } function edge(source: string, target: string): SceneGraphEdge { return { sourceGraphNodeId: asGraphNodeId(source), targetGraphNodeId: asGraphNodeId(target) }; } function minimalProject(partial: Partial & Pick): Project { return { id: partial.id ?? ('p1' as Project['id']), title: partial.title ?? 'Test', scenes: partial.scenes ?? {}, sceneListOrder: partial.sceneListOrder ?? [], sceneGraphNodes: partial.sceneGraphNodes, sceneGraphEdges: partial.sceneGraphEdges, currentGraphNodeId: partial.currentGraphNodeId ?? null, currentSceneId: partial.currentSceneId ?? null, materials: partial.materials ?? [], campaignAudio: partial.campaignAudio ?? [], npcs: partial.npcs ?? [], npcGroups: partial.npcGroups ?? [], npcRelations: partial.npcRelations ?? [], }; } void test('listActiveStorylineScenes: main line ordered by sceneListOrder, deduped', () => { const s1 = asSceneId('s1'); const s2 = asSceneId('s2'); const s3 = asSceneId('s3'); const nodes = [ node('main', 's1', { isStartScene: true }), node('a', 's2'), node('b', 's3'), node('dup', 's2'), ]; const edges = [edge('main', 'a'), edge('a', 'b'), edge('a', 'dup')]; const project = minimalProject({ scenes: { [s1]: { id: s1, title: 'Start' }, [s2]: { id: s2, title: 'Mid' }, [s3]: { id: s3, title: 'End' }, }, sceneListOrder: [s3, s1, s2], sceneGraphNodes: nodes, sceneGraphEdges: edges, currentGraphNodeId: asGraphNodeId('a'), }); const list = listActiveStorylineScenes(project); assert.deepEqual( list.map((e) => e.sceneId), [s3, s1, s2], ); assert.equal(list[2]?.graphNodeId, asGraphNodeId('a')); }); void test('listActiveStorylineScenes: side line only', () => { const s1 = asSceneId('s1'); const s2 = asSceneId('s2'); const sSide = asSceneId('sSide'); const nodes = [ node('main', 's1', { isStartScene: true }), node('side', 'sSide', { isSideStoryStart: true }), node('x', 's2'), ]; const edges = [edge('side', 'x')]; const project = minimalProject({ scenes: { [s1]: { id: s1, title: 'Main' }, [s2]: { id: s2, title: 'Side step' }, [sSide]: { id: sSide, title: 'Side start' }, }, sceneListOrder: [s1, s2, sSide], sceneGraphNodes: nodes, sceneGraphEdges: edges, currentGraphNodeId: asGraphNodeId('x'), }); const ids = getActiveStorylineGraphNodeIds(nodes, edges, asGraphNodeId('x')); assert.deepEqual([...ids].sort(), [asGraphNodeId('side'), asGraphNodeId('x')].sort()); const list = listActiveStorylineScenes(project); assert.deepEqual( list.map((e) => e.sceneId), [s2, sSide], ); }); void test('listActiveStorylineScenes: current node preferred for duplicate sceneId', () => { const s2 = asSceneId('s2'); const nodes = [ node('main', 's1', { isStartScene: true }), node('a', 's2'), node('dup', 's2'), ]; const edges = [edge('main', 'a'), edge('main', 'dup')]; const project = minimalProject({ scenes: { [asSceneId('s1')]: { id: asSceneId('s1'), title: 'S1' }, [s2]: { id: s2, title: 'S2' } }, sceneListOrder: [asSceneId('s1'), s2], sceneGraphNodes: nodes, sceneGraphEdges: edges, currentGraphNodeId: asGraphNodeId('dup'), }); const list = listActiveStorylineScenes(project); assert.equal(list.find((e) => e.sceneId === s2)?.graphNodeId, asGraphNodeId('dup')); });