986 lines
24 KiB
TypeScript
986 lines
24 KiB
TypeScript
import type {Edge} from "@xyflow/react";
|
|
import graphReducer, {
|
|
defaultGraphPreprocessor, defaultPhaseReducer,
|
|
orderPhases
|
|
} from "../../../../src/pages/VisProgPage/visualProgrammingUI/GraphReducer.ts";
|
|
import type {PreparedPhase} from "../../../../src/pages/VisProgPage/visualProgrammingUI/GraphReducerTypes.ts";
|
|
import useFlowStore from "../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx";
|
|
import type {AppNode} from "../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgTypes.tsx";
|
|
|
|
// sets of default values for nodes and edges to be used for test cases
|
|
type FlowState = {
|
|
name: string;
|
|
nodes: AppNode[];
|
|
edges: Edge[];
|
|
};
|
|
|
|
// predefined graphs for testing:
|
|
const onlyOnePhase : FlowState = {
|
|
name: "onlyOnePhase",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'phase-1-end',
|
|
source: 'phase-1',
|
|
target: 'end',
|
|
}
|
|
]
|
|
};
|
|
const onlyThreePhases : FlowState = {
|
|
name: "onlyThreePhases",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'phase-1-phase-2',
|
|
source: 'phase-1',
|
|
target: 'phase-2',
|
|
},
|
|
{
|
|
id: 'phase-2-phase-3',
|
|
source: 'phase-2',
|
|
target: 'phase-3',
|
|
},
|
|
{
|
|
id: 'phase-3-end',
|
|
source: 'phase-3',
|
|
target: 'end',
|
|
}
|
|
]
|
|
};
|
|
const onlySingleEdgeNorms : FlowState = {
|
|
name: "onlySingleEdgeNorms",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'norm-1',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
},
|
|
{
|
|
id: 'norm-2',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'norm-1-phase-2',
|
|
source: 'norm-1',
|
|
target: 'phase-2',
|
|
},
|
|
{
|
|
id: 'phase-1-phase-2',
|
|
source: 'phase-1',
|
|
target: 'phase-2',
|
|
},
|
|
{
|
|
id: 'phase-2-phase-3',
|
|
source: 'phase-2',
|
|
target: 'phase-3',
|
|
},
|
|
{
|
|
id: 'norm-2-phase-3',
|
|
source: 'norm-2',
|
|
target: 'phase-3',
|
|
},
|
|
{
|
|
id: 'phase-3-end',
|
|
source: 'phase-3',
|
|
target: 'end',
|
|
}
|
|
]
|
|
};
|
|
const multiEdgeNorms : FlowState = {
|
|
name: "multiEdgeNorms",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'norm-1',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
},
|
|
{
|
|
id: 'norm-2',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
},
|
|
{
|
|
id: 'norm-3',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'norm-1-phase-2',
|
|
source: 'norm-1',
|
|
target: 'phase-2',
|
|
},
|
|
{
|
|
id: 'norm-1-phase-3',
|
|
source: 'norm-1',
|
|
target: 'phase-3',
|
|
},
|
|
{
|
|
id: 'phase-1-phase-2',
|
|
source: 'phase-1',
|
|
target: 'phase-2',
|
|
},
|
|
{
|
|
id: 'norm-3-phase-1',
|
|
source: 'norm-3',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'phase-2-phase-3',
|
|
source: 'phase-2',
|
|
target: 'phase-3',
|
|
},
|
|
{
|
|
id: 'norm-2-phase-3',
|
|
source: 'norm-2',
|
|
target: 'phase-3',
|
|
},
|
|
{
|
|
id: 'norm-2-phase-2',
|
|
source: 'norm-2',
|
|
target: 'phase-2',
|
|
},
|
|
{
|
|
id: 'phase-3-end',
|
|
source: 'phase-3',
|
|
target: 'end',
|
|
}
|
|
]
|
|
};
|
|
const onlyStartEnd : FlowState = {
|
|
name: "onlyStartEnd",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-end',
|
|
source: 'start',
|
|
target: 'end',
|
|
},
|
|
]
|
|
};
|
|
|
|
// states that contain invalid programs for testing if correct errors are thrown:
|
|
const phaseConnectsToInvalidNodeType : FlowState = {
|
|
name: "phaseConnectsToInvalidNodeType",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'default-1',
|
|
type: 'default',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm'},
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'phase-1-default-1',
|
|
source: 'phase-1',
|
|
target: 'default-1',
|
|
},
|
|
]
|
|
};
|
|
const phaseHasNoOutgoingConnections : FlowState = {
|
|
name: "phaseHasNoOutgoingConnections",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
]
|
|
};
|
|
const phaseHasTooManyOutgoingConnections : FlowState = {
|
|
name: "phaseHasTooManyOutgoingConnections",
|
|
nodes: [
|
|
{
|
|
id: 'start',
|
|
type: 'start',
|
|
position: {x: 0, y: 0},
|
|
data: {label: 'start'}
|
|
},
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
{
|
|
id: 'end',
|
|
type: 'end',
|
|
position: {x: 0, y: 300},
|
|
data: {label: 'End'}
|
|
}
|
|
],
|
|
edges:[
|
|
{
|
|
id: 'start-phase-1',
|
|
source: 'start',
|
|
target: 'phase-1',
|
|
},
|
|
{
|
|
id: 'phase-1-phase-2',
|
|
source: 'phase-1',
|
|
target: 'phase-2',
|
|
},
|
|
{
|
|
id: 'phase-1-end',
|
|
source: 'phase-1',
|
|
target: 'end',
|
|
},
|
|
{
|
|
id: 'phase-2-end',
|
|
source: 'phase-2',
|
|
target: 'end',
|
|
},
|
|
]
|
|
};
|
|
|
|
describe('Graph Reducer Tests', () => {
|
|
describe('defaultGraphPreprocessor', () => {
|
|
test.each([
|
|
{
|
|
state: onlyOnePhase,
|
|
expected: [
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
nextPhaseId: 'end',
|
|
connectedNorms: [],
|
|
connectedGoals: [],
|
|
}]
|
|
},
|
|
{
|
|
state: onlyThreePhases,
|
|
expected: [
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
nextPhaseId: 'phase-2',
|
|
connectedNorms: [],
|
|
connectedGoals: [],
|
|
},
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
nextPhaseId: 'phase-3',
|
|
connectedNorms: [],
|
|
connectedGoals: [],
|
|
},
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
},
|
|
nextPhaseId: 'end',
|
|
connectedNorms: [],
|
|
connectedGoals: [],
|
|
}]
|
|
},
|
|
{
|
|
state: onlySingleEdgeNorms,
|
|
expected: [
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
nextPhaseId: 'phase-2',
|
|
connectedNorms: [],
|
|
connectedGoals: [],
|
|
},
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
nextPhaseId: 'phase-3',
|
|
connectedNorms: [{
|
|
id: 'norm-1',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
}],
|
|
connectedGoals: [],
|
|
},
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
},
|
|
nextPhaseId: 'end',
|
|
connectedNorms: [{
|
|
id: 'norm-2',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
}],
|
|
connectedGoals: [],
|
|
}]
|
|
},
|
|
{
|
|
state: multiEdgeNorms,
|
|
expected: [
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
nextPhaseId: 'phase-2',
|
|
connectedNorms: [{
|
|
id: 'norm-3',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
}],
|
|
connectedGoals: [],
|
|
},
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
nextPhaseId: 'phase-3',
|
|
connectedNorms: [{
|
|
id: 'norm-1',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
},
|
|
{
|
|
id: 'norm-2',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
}],
|
|
connectedGoals: [],
|
|
},
|
|
{
|
|
phaseNode: {
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
},
|
|
nextPhaseId: 'end',
|
|
connectedNorms: [{
|
|
id: 'norm-1',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
},
|
|
{
|
|
id: 'norm-2',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
}],
|
|
connectedGoals: [],
|
|
}]
|
|
},
|
|
{
|
|
state: onlyStartEnd,
|
|
expected: [],
|
|
}
|
|
])(`tests state: $state.name`, ({state, expected}) => {
|
|
const output = defaultGraphPreprocessor(state.nodes, state.edges);
|
|
expect(output).toEqual(expected);
|
|
});
|
|
});
|
|
describe("orderPhases", () => {
|
|
test.each([
|
|
{
|
|
state: onlyOnePhase,
|
|
expected: {
|
|
phaseNodes: [{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
}],
|
|
connections: new Map<string,string>([["phase-1","end"]])
|
|
}
|
|
},
|
|
{
|
|
state: onlyThreePhases,
|
|
expected: {
|
|
phaseNodes: [
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
}],
|
|
connections: new Map<string,string>([
|
|
["phase-1","phase-2"],
|
|
["phase-2","phase-3"],
|
|
["phase-3","end"]
|
|
])
|
|
}
|
|
},
|
|
{
|
|
state: onlySingleEdgeNorms,
|
|
expected: {
|
|
phaseNodes: [
|
|
{
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 2},
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 3},
|
|
}],
|
|
connections: new Map<string,string>([
|
|
["phase-1","phase-2"],
|
|
["phase-2","phase-3"],
|
|
["phase-3","end"]
|
|
])
|
|
}
|
|
},
|
|
{
|
|
state: onlyStartEnd,
|
|
expected: {
|
|
phaseNodes: [],
|
|
connections: new Map<string,string>()
|
|
}
|
|
}
|
|
])(`tests state: $state.name`, ({state, expected}) => {
|
|
const output = orderPhases(state.nodes, state.edges);
|
|
expect(output.phaseNodes).toEqual(expected.phaseNodes);
|
|
expect(output.connections).toEqual(expected.connections);
|
|
});
|
|
test.each([
|
|
{
|
|
state: phaseConnectsToInvalidNodeType,
|
|
expected: new Error('| INVALID PROGRAM | the node "default-1" that "phase-1" connects to is not a phase or end node')
|
|
},
|
|
{
|
|
state: phaseHasNoOutgoingConnections,
|
|
expected: new Error('| INVALID PROGRAM | the source handle of "phase-1" doesn\'t have any outgoing connections')
|
|
},
|
|
{
|
|
state: phaseHasTooManyOutgoingConnections,
|
|
expected: new Error('| INVALID PROGRAM | the source handle of "phase-1" connects to too many targets')
|
|
}
|
|
])(`tests erroneous state: $state.name`, ({state, expected}) => {
|
|
const testForError = () => {
|
|
orderPhases(state.nodes, state.edges);
|
|
};
|
|
expect(testForError).toThrow(expected);
|
|
})
|
|
})
|
|
describe("defaultPhaseReducer", () => {
|
|
test("phaseReducer handles empty norms and goals without failing", () => {
|
|
const input : PreparedPhase = {
|
|
phaseNode: {
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
nextPhaseId: 'end',
|
|
connectedNorms: [],
|
|
connectedGoals: [],
|
|
}
|
|
const output = defaultPhaseReducer(input);
|
|
expect(output).toEqual({
|
|
id: 'phase-1',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'end',
|
|
phaseData: {
|
|
norms: [],
|
|
goals: []
|
|
}
|
|
});
|
|
});
|
|
test("defaultNormReducer reduces norms correctly", () => {
|
|
const input : PreparedPhase = {
|
|
phaseNode: {
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
nextPhaseId: 'end',
|
|
connectedNorms: [{
|
|
id: 'norm-1',
|
|
type: 'norm',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Norm', value: "generic"},
|
|
}],
|
|
connectedGoals: [],
|
|
}
|
|
const output = defaultPhaseReducer(input);
|
|
expect(output).toEqual({
|
|
id: 'phase-1',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'end',
|
|
phaseData: {
|
|
norms: [{
|
|
id: 'norm-1',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
}],
|
|
goals: []
|
|
}
|
|
});
|
|
});
|
|
test("defaultGoalReducer reduces goals correctly", () => {
|
|
const input : PreparedPhase = {
|
|
phaseNode: {
|
|
id: 'phase-1',
|
|
type: 'phase',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Phase', number: 1},
|
|
},
|
|
nextPhaseId: 'end',
|
|
connectedNorms: [],
|
|
connectedGoals: [{
|
|
id: 'goal-1',
|
|
type: 'goal',
|
|
position: {x: 0, y: 150},
|
|
data: {label: 'Generic Goal', value: "generic"},
|
|
}],
|
|
}
|
|
const output = defaultPhaseReducer(input);
|
|
expect(output).toEqual({
|
|
id: 'phase-1',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'end',
|
|
phaseData: {
|
|
norms: [],
|
|
goals: [{
|
|
id: 'goal-1',
|
|
name: 'Generic Goal',
|
|
value: "generic"
|
|
}]
|
|
}
|
|
});
|
|
});
|
|
})
|
|
describe("GraphReducer", () => {
|
|
test.each([
|
|
{
|
|
state: onlyOnePhase,
|
|
expected: [
|
|
{
|
|
id: 'phase-1',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'end',
|
|
phaseData: {
|
|
norms: [],
|
|
goals: []
|
|
}
|
|
}]
|
|
},
|
|
{
|
|
state: onlyThreePhases,
|
|
expected: [
|
|
{
|
|
id: 'phase-1',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'phase-2',
|
|
phaseData: {
|
|
norms: [],
|
|
goals: []
|
|
}
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'phase-3',
|
|
phaseData: {
|
|
norms: [],
|
|
goals: []
|
|
}
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'end',
|
|
phaseData: {
|
|
norms: [],
|
|
goals: []
|
|
}
|
|
}]
|
|
},
|
|
{
|
|
state: onlySingleEdgeNorms,
|
|
expected: [
|
|
{
|
|
id: 'phase-1',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'phase-2',
|
|
phaseData: {
|
|
norms: [],
|
|
goals: []
|
|
}
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'phase-3',
|
|
phaseData: {
|
|
norms: [
|
|
{
|
|
id: 'norm-1',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
}
|
|
],
|
|
goals: []
|
|
}
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'end',
|
|
phaseData: {
|
|
norms: [{
|
|
id: 'norm-2',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
}],
|
|
goals: []
|
|
}
|
|
}]
|
|
},
|
|
{
|
|
state: multiEdgeNorms,
|
|
expected: [
|
|
{
|
|
id: 'phase-1',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'phase-2',
|
|
phaseData: {
|
|
norms: [{
|
|
id: 'norm-3',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
}],
|
|
goals: []
|
|
}
|
|
},
|
|
{
|
|
id: 'phase-2',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'phase-3',
|
|
phaseData: {
|
|
norms: [
|
|
{
|
|
id: 'norm-1',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
},
|
|
{
|
|
id: 'norm-2',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
}
|
|
],
|
|
goals: []
|
|
}
|
|
},
|
|
{
|
|
id: 'phase-3',
|
|
name: 'Generic Phase',
|
|
nextPhaseId: 'end',
|
|
phaseData: {
|
|
norms: [{
|
|
id: 'norm-1',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
},
|
|
{
|
|
id: 'norm-2',
|
|
name: 'Generic Norm',
|
|
value: "generic"
|
|
}],
|
|
goals: []
|
|
}
|
|
}]
|
|
},
|
|
{
|
|
state: onlyStartEnd,
|
|
expected: [],
|
|
}
|
|
])("`tests state: $state.name`", ({state, expected}) => {
|
|
useFlowStore.setState({nodes: state.nodes, edges: state.edges});
|
|
const output = graphReducer(); // uses default reducers
|
|
expect(output).toEqual(expected);
|
|
})
|
|
// we run the test for correct error handling for the entire graph reducer as well,
|
|
// to make sure no errors occur before we intend to handle the errors ourselves
|
|
test.each([
|
|
{
|
|
state: phaseConnectsToInvalidNodeType,
|
|
expected: new Error('| INVALID PROGRAM | the node "default-1" that "phase-1" connects to is not a phase or end node')
|
|
},
|
|
{
|
|
state: phaseHasNoOutgoingConnections,
|
|
expected: new Error('| INVALID PROGRAM | the source handle of "phase-1" doesn\'t have any outgoing connections')
|
|
},
|
|
{
|
|
state: phaseHasTooManyOutgoingConnections,
|
|
expected: new Error('| INVALID PROGRAM | the source handle of "phase-1" connects to too many targets')
|
|
}
|
|
])(`tests erroneous state: $state.name`, ({state, expected}) => {
|
|
useFlowStore.setState({nodes: state.nodes, edges: state.edges});
|
|
const testForError = () => {
|
|
graphReducer();
|
|
};
|
|
expect(testForError).toThrow(expected);
|
|
})
|
|
})
|
|
}); |