feat: simplify paths
This commit is contained in:
@@ -7,7 +7,6 @@ import {
|
||||
} from './canvasStore'
|
||||
import {
|
||||
selectPathNodeIds,
|
||||
selectPathPausedSegmentNodeIds,
|
||||
selectPathActiveSegmentNodeIds,
|
||||
selectConnectionStatusForEdge,
|
||||
selectPathRoleForNode,
|
||||
@@ -141,42 +140,15 @@ describe('canvasStoreReducer', () => {
|
||||
expect(next.path.triggerNodeIds).toEqual([])
|
||||
})
|
||||
|
||||
it('path/startUpdate adds to updatingNodeIds', () => {
|
||||
it('path/addTrigger sets pulseEndsAt', () => {
|
||||
const state: CanvasStore = { ...initialCanvasStore }
|
||||
const before = Date.now()
|
||||
const next = canvasStoreReducer(state, {
|
||||
type: 'path/startUpdate',
|
||||
type: 'path/addTrigger',
|
||||
payload: 'n1',
|
||||
})
|
||||
expect(next.path.updatingNodeIds).toEqual(['n1'])
|
||||
})
|
||||
|
||||
it('path/endUpdate removes from updatingNodeIds', () => {
|
||||
const state: CanvasStore = {
|
||||
...initialCanvasStore,
|
||||
path: {
|
||||
...initialCanvasStore.path,
|
||||
updatingNodeIds: ['n1', 'n2'],
|
||||
},
|
||||
}
|
||||
const next = canvasStoreReducer(state, {
|
||||
type: 'path/endUpdate',
|
||||
payload: 'n1',
|
||||
})
|
||||
expect(next.path.updatingNodeIds).toEqual(['n2'])
|
||||
})
|
||||
|
||||
it('path/setPaused adds and removes paused node', () => {
|
||||
const state: CanvasStore = { ...initialCanvasStore }
|
||||
let next = canvasStoreReducer(state, {
|
||||
type: 'path/setPaused',
|
||||
payload: { nodeId: 'n1', paused: true },
|
||||
})
|
||||
expect(next.path.pausedNodeIds).toEqual(['n1'])
|
||||
next = canvasStoreReducer(next, {
|
||||
type: 'path/setPaused',
|
||||
payload: { nodeId: 'n1', paused: false },
|
||||
})
|
||||
expect(next.path.pausedNodeIds).toEqual([])
|
||||
expect(next.path.triggerNodeIds).toEqual(['n1'])
|
||||
expect(next.path.pulseEndsAt).toBeGreaterThanOrEqual(before + 1000)
|
||||
})
|
||||
|
||||
it('path/setError adds and removes error node', () => {
|
||||
@@ -193,20 +165,18 @@ describe('canvasStoreReducer', () => {
|
||||
expect(next.path.errorNodeIds).toEqual([])
|
||||
})
|
||||
|
||||
it('path/clearPathSession resets updating, trigger, paused; keeps error', () => {
|
||||
it('path/clearPathSession resets trigger and pulse; keeps error', () => {
|
||||
const state: CanvasStore = {
|
||||
...initialCanvasStore,
|
||||
path: {
|
||||
updatingNodeIds: ['u1'],
|
||||
triggerNodeIds: ['t1'],
|
||||
pausedNodeIds: ['p1'],
|
||||
pulseEndsAt: Date.now() + 1000,
|
||||
errorNodeIds: ['e1'],
|
||||
},
|
||||
}
|
||||
const next = canvasStoreReducer(state, { type: 'path/clearPathSession' })
|
||||
expect(next.path.updatingNodeIds).toEqual([])
|
||||
expect(next.path.triggerNodeIds).toEqual([])
|
||||
expect(next.path.pausedNodeIds).toEqual([])
|
||||
expect(next.path.pulseEndsAt).toBeNull()
|
||||
expect(next.path.errorNodeIds).toEqual(['e1'])
|
||||
})
|
||||
})
|
||||
@@ -280,9 +250,8 @@ describe('canvasStore selectors', () => {
|
||||
],
|
||||
},
|
||||
path: {
|
||||
updatingNodeIds: ['c'],
|
||||
triggerNodeIds: ['a'],
|
||||
pausedNodeIds: [],
|
||||
pulseEndsAt: null,
|
||||
errorNodeIds: [],
|
||||
},
|
||||
}
|
||||
@@ -311,15 +280,14 @@ describe('canvasStore selectors', () => {
|
||||
path: {
|
||||
...initialCanvasStore.path,
|
||||
triggerNodeIds: ['a'],
|
||||
updatingNodeIds: ['b'],
|
||||
pausedNodeIds: [],
|
||||
pulseEndsAt: null,
|
||||
errorNodeIds: ['b'],
|
||||
},
|
||||
}
|
||||
expect(selectConnectionStatusForEdge(state, 'a', 'b')).toBe('error')
|
||||
})
|
||||
|
||||
it('selectPathRoleForNode returns trigger when node in triggerNodeIds', () => {
|
||||
it('selectPathRoleForNode returns trigger or on-path', () => {
|
||||
const state: CanvasStore = {
|
||||
...initialCanvasStore,
|
||||
graph: {
|
||||
@@ -329,13 +297,12 @@ describe('canvasStore selectors', () => {
|
||||
path: {
|
||||
...initialCanvasStore.path,
|
||||
triggerNodeIds: ['a'],
|
||||
updatingNodeIds: ['b'],
|
||||
pausedNodeIds: [],
|
||||
pulseEndsAt: null,
|
||||
errorNodeIds: [],
|
||||
},
|
||||
}
|
||||
expect(selectPathRoleForNode(state, 'a')).toBe('trigger')
|
||||
expect(selectPathRoleForNode(state, 'b')).toBe('updating')
|
||||
expect(selectPathRoleForNode(state, 'b')).toBe('on-path')
|
||||
expect(selectPathRoleForNode(state, 'x')).toBe(null)
|
||||
})
|
||||
|
||||
@@ -378,14 +345,13 @@ describe('canvas store integration', () => {
|
||||
expect(state.graph.nodes[0].id).toBe('test-1')
|
||||
})
|
||||
|
||||
it('dispatch path/addTrigger updates path and selectPathNodeIds', () => {
|
||||
it('dispatch path/addTrigger updates path and starts pulse', () => {
|
||||
dispatchCanvasCommand({ type: 'graph/setNodes', payload: [makeNode('a'), makeNode('b')] })
|
||||
dispatchCanvasCommand({ type: 'graph/setEdges', payload: [makeEdge('e1', 'a', 'b')] })
|
||||
dispatchCanvasCommand({ type: 'path/addTrigger', payload: 'a' })
|
||||
dispatchCanvasCommand({ type: 'path/startUpdate', payload: 'b' })
|
||||
const state = getCanvasStore()
|
||||
expect(state.path.triggerNodeIds).toContain('a')
|
||||
expect(state.path.updatingNodeIds).toContain('b')
|
||||
expect(state.path.pulseEndsAt).not.toBeNull()
|
||||
const pathIds = selectPathNodeIds(state)
|
||||
expect(pathIds.has('a')).toBe(true)
|
||||
expect(pathIds.has('b')).toBe(true)
|
||||
@@ -396,4 +362,53 @@ describe('canvas store integration', () => {
|
||||
const state = getCanvasStore()
|
||||
expect(state.ui.fullscreenNodeId).toBe('full-node')
|
||||
})
|
||||
|
||||
// Pulse: addTrigger starts a short "updating" pulse along downstream path.
|
||||
it('path-per-sink: addTrigger starts pulse, all downstream edges show updating', () => {
|
||||
const nodes: AppNode[] = [
|
||||
{
|
||||
id: 'var_001',
|
||||
type: 'variable',
|
||||
position: { x: -270, y: 210 },
|
||||
data: { value: 'sss', valueType: 'string' },
|
||||
},
|
||||
{
|
||||
id: 'cfg_001',
|
||||
type: 'config',
|
||||
position: { x: 180, y: 330 },
|
||||
data: { configType: 'plantuml', content: '@startuml\nactor Mulis\n@enduml\n', title: 'cfg_001' },
|
||||
},
|
||||
{
|
||||
id: 'rnd_001',
|
||||
type: 'render',
|
||||
position: { x: 780, y: 540 },
|
||||
data: { updateMode: 'manual', runTrigger: 1, lastRunSourceSignature: 'sig1' },
|
||||
},
|
||||
{
|
||||
id: 'rnd_002',
|
||||
type: 'render',
|
||||
position: { x: 780, y: 180 },
|
||||
data: { updateMode: 'auto', runTrigger: 2, lastRunSourceSignature: 'sig2' },
|
||||
},
|
||||
]
|
||||
const edges: AppEdge[] = [
|
||||
{ id: 'xy-edge__var_001out-cfg_001ain', source: 'var_001', target: 'cfg_001', data: { targetType: 'config' } },
|
||||
{ id: 'xy-edge__cfg_001out-rnd_001ain', source: 'cfg_001', target: 'rnd_001', data: { targetType: 'render' } },
|
||||
{ id: 'xy-edge__cfg_001out-rnd_002ain', source: 'cfg_001', target: 'rnd_002', data: { targetType: 'render' } },
|
||||
]
|
||||
dispatchCanvasCommand({ type: 'graph/apply', payload: { nodes, edges } })
|
||||
dispatchCanvasCommand({ type: 'path/addTrigger', payload: 'var_001' })
|
||||
|
||||
const state = getCanvasStore()
|
||||
const pathIds = selectPathNodeIds(state)
|
||||
expect(pathIds.has('var_001')).toBe(true)
|
||||
expect(pathIds.has('cfg_001')).toBe(true)
|
||||
expect(pathIds.has('rnd_001')).toBe(true)
|
||||
expect(pathIds.has('rnd_002')).toBe(true)
|
||||
|
||||
// During pulse, all path edges show "updating".
|
||||
expect(selectConnectionStatusForEdge(state, 'var_001', 'cfg_001')).toBe('updating')
|
||||
expect(selectConnectionStatusForEdge(state, 'cfg_001', 'rnd_001')).toBe('updating')
|
||||
expect(selectConnectionStatusForEdge(state, 'cfg_001', 'rnd_002')).toBe('updating')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user