Conditional Norms
This commit is contained in:
committed by
Gerla, J. (Justin)
parent
0ad2d5935f
commit
4e9a048c90
@@ -10,8 +10,9 @@ import NormNode, {
|
||||
import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores';
|
||||
import type { Node } from '@xyflow/react';
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
|
||||
import { NormNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/NormNode.default.ts';
|
||||
import { BasicBeliefNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.default.ts';
|
||||
import BasicBeliefNode, { BasicBeliefConnectionSource } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.tsx';
|
||||
|
||||
describe('NormNode', () => {
|
||||
let user: ReturnType<typeof userEvent.setup>;
|
||||
@@ -26,12 +27,7 @@ describe('NormNode', () => {
|
||||
id: 'norm-1',
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
hasReduce: true,
|
||||
},
|
||||
data: {...JSON.parse(JSON.stringify(NormNodeDefaults))},
|
||||
};
|
||||
|
||||
renderWithProviders(
|
||||
@@ -60,6 +56,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'Be respectful to humans',
|
||||
@@ -94,8 +91,10 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
conditions: [],
|
||||
norm: '',
|
||||
hasReduce: true,
|
||||
critical: false
|
||||
@@ -129,6 +128,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'Dragged norm',
|
||||
@@ -165,6 +165,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
@@ -210,6 +211,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'Initial norm text',
|
||||
@@ -261,6 +263,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
@@ -314,6 +317,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
@@ -358,6 +362,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
@@ -404,6 +409,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Safety Norm',
|
||||
droppable: true,
|
||||
norm: 'Never harm humans',
|
||||
@@ -418,6 +424,8 @@ describe('NormNode', () => {
|
||||
id: 'norm-1',
|
||||
label: 'Safety Norm',
|
||||
norm: 'Never harm humans',
|
||||
critical: false,
|
||||
basic_beliefs: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -427,6 +435,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Norm 1',
|
||||
droppable: true,
|
||||
norm: 'Be helpful',
|
||||
@@ -439,6 +448,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 100, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Norm 2',
|
||||
droppable: true,
|
||||
norm: 'Be honest',
|
||||
@@ -463,6 +473,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Empty Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
@@ -482,6 +493,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Custom Label',
|
||||
droppable: false,
|
||||
norm: 'Test norm',
|
||||
@@ -502,6 +514,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'Test',
|
||||
@@ -514,6 +527,7 @@ describe('NormNode', () => {
|
||||
type: 'phase',
|
||||
position: { x: 100, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Phase 1',
|
||||
droppable: true,
|
||||
children: [],
|
||||
@@ -532,6 +546,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'Test',
|
||||
@@ -544,6 +559,7 @@ describe('NormNode', () => {
|
||||
type: 'phase',
|
||||
position: { x: 100, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Phase 1',
|
||||
droppable: true,
|
||||
children: [],
|
||||
@@ -562,6 +578,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...NormNodeDefaults,
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'Test',
|
||||
@@ -583,6 +600,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
@@ -634,6 +652,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: '',
|
||||
@@ -682,6 +701,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Norm 1',
|
||||
droppable: true,
|
||||
norm: 'Original norm 1',
|
||||
@@ -694,6 +714,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 100, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Norm 2',
|
||||
droppable: true,
|
||||
norm: 'Original norm 2',
|
||||
@@ -748,6 +769,7 @@ describe('NormNode', () => {
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...NormNodeDefaults,
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'haa haa fuyaaah - link',
|
||||
@@ -778,21 +800,154 @@ describe('NormNode', () => {
|
||||
);
|
||||
|
||||
const input = screen.getByPlaceholderText('Pepper should ...');
|
||||
expect(input).toBeDefined()
|
||||
|
||||
await user.type(input, 'a');
|
||||
await user.type(input, 'a{enter}');
|
||||
await waitFor(() => {
|
||||
expect(useFlowStore.getState().nodes[0].data.norm).toBe('haa haa fuyaaah - link');
|
||||
expect(useFlowStore.getState().nodes[0].data.norm).toBe('haa haa fuyaaah - linka');
|
||||
});
|
||||
|
||||
await user.type(input, 'b');
|
||||
await user.type(input, 'b{enter}');
|
||||
await waitFor(() => {
|
||||
expect(useFlowStore.getState().nodes[0].data.norm).toBe('haa haa fuyaaah - link');
|
||||
expect(useFlowStore.getState().nodes[0].data.norm).toBe('haa haa fuyaaah - linkab');
|
||||
});
|
||||
|
||||
await user.type(input, 'c');
|
||||
await user.type(input, 'c{enter}');
|
||||
await waitFor(() => {
|
||||
expect(useFlowStore.getState().nodes[0].data.norm).toBe('haa haa fuyaaah - link');
|
||||
}, { timeout: 3000 });
|
||||
expect(useFlowStore.getState().nodes[0].data.norm).toBe('haa haa fuyaaah - linkabc');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Integration beliefs', () => {
|
||||
it('should update visually when adding beliefs', async () => {
|
||||
// Setup state
|
||||
const mockNode: Node = {
|
||||
id: 'norm-1',
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'haa haa fuyaaah - link',
|
||||
hasReduce: true,
|
||||
}
|
||||
};
|
||||
|
||||
const mockBelief: Node = {
|
||||
id: 'basic_belief-1',
|
||||
type: 'basic_belief',
|
||||
position: {x:100, y:100},
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(BasicBeliefNodeDefaults))
|
||||
}
|
||||
};
|
||||
|
||||
useFlowStore.setState({
|
||||
nodes: [mockNode, mockBelief],
|
||||
edges: [],
|
||||
});
|
||||
|
||||
// Simulate connecting
|
||||
NormConnectionTarget(mockNode, mockBelief.id);
|
||||
BasicBeliefConnectionSource(mockBelief, mockNode.id)
|
||||
|
||||
renderWithProviders(
|
||||
<div>
|
||||
<NormNode
|
||||
id={mockNode.id}
|
||||
type={mockNode.type as string}
|
||||
data={mockNode.data as any}
|
||||
selected={false}
|
||||
isConnectable={true}
|
||||
zIndex={0}
|
||||
dragging={false}
|
||||
selectable={true}
|
||||
deletable={true}
|
||||
draggable={true}
|
||||
positionAbsoluteX={0}
|
||||
positionAbsoluteY={0}
|
||||
/>
|
||||
<BasicBeliefNode
|
||||
id={mockBelief.id}
|
||||
type={mockBelief.type as string}
|
||||
data={mockBelief.data as any}
|
||||
selected={false}
|
||||
isConnectable={true}
|
||||
zIndex={0}
|
||||
dragging={false}
|
||||
selectable={true}
|
||||
deletable={true}
|
||||
draggable={true}
|
||||
positionAbsoluteX={0}
|
||||
positionAbsoluteY={0}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('norm-condition-information')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('should update the data when adding beliefs', async () => {
|
||||
// Setup state
|
||||
const mockNode: Node = {
|
||||
id: 'norm-1',
|
||||
type: 'norm',
|
||||
position: { x: 0, y: 0 },
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(NormNodeDefaults)),
|
||||
label: 'Test Norm',
|
||||
droppable: true,
|
||||
norm: 'haa haa fuyaaah - link',
|
||||
hasReduce: true,
|
||||
}
|
||||
};
|
||||
|
||||
const mockBelief1: Node = {
|
||||
id: 'basic_belief-1',
|
||||
type: 'basic_belief',
|
||||
position: {x:100, y:100},
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(BasicBeliefNodeDefaults))
|
||||
}
|
||||
};
|
||||
|
||||
const mockBelief2: Node = {
|
||||
id: 'basic_belief-2',
|
||||
type: 'basic_belief',
|
||||
position: {x:300, y:300},
|
||||
data: {
|
||||
...JSON.parse(JSON.stringify(BasicBeliefNodeDefaults))
|
||||
}
|
||||
};
|
||||
|
||||
useFlowStore.setState({
|
||||
nodes: [mockNode, mockBelief1, mockBelief2],
|
||||
edges: [],
|
||||
});
|
||||
|
||||
// Simulate connecting
|
||||
useFlowStore.getState().onConnect({
|
||||
source: 'basic_belief-1',
|
||||
target: 'norm-1',
|
||||
sourceHandle: null,
|
||||
targetHandle: null,
|
||||
});
|
||||
useFlowStore.getState().onConnect({
|
||||
source: 'basic_belief-2',
|
||||
target: 'norm-1',
|
||||
sourceHandle: null,
|
||||
targetHandle: null,
|
||||
});
|
||||
|
||||
const state = useFlowStore.getState();
|
||||
const updatedNorm = state.nodes.find(n => n.id === 'norm-1');
|
||||
expect(updatedNorm?.data.conditions).toEqual(["basic_belief-1", "basic_belief-2"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import { createElement } from 'react';
|
||||
import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgrammingUI/VisProgStores';
|
||||
|
||||
|
||||
describe('NormNode', () => {
|
||||
describe('Universal Nodes', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
@@ -107,6 +107,50 @@ describe('NormNode', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Disconnecting', () => {
|
||||
test.each(getAllTypes())('it should remove the correct data when something is disconnected on a %s node.', (nodeType) => {
|
||||
// Create two nodes - one of the current type and one to connect to
|
||||
const sourceNode = createNode('source-1', nodeType, {x: 100, y: 100}, {});
|
||||
const targetNode = createNode('target-1', 'basic_belief', {x: 300, y: 100}, {});
|
||||
|
||||
// Add nodes to store
|
||||
useFlowStore.setState({ nodes: [sourceNode, targetNode] });
|
||||
|
||||
// Spy on the connect functions
|
||||
const sourceConnectSpy = jest.spyOn(NodeConnections.Sources, nodeType as keyof typeof NodeConnections.Sources);
|
||||
const targetConnectSpy = jest.spyOn(NodeConnections.Targets, 'basic_belief');
|
||||
|
||||
// Simulate connection
|
||||
useFlowStore.getState().onConnect({
|
||||
source: 'source-1',
|
||||
target: 'target-1',
|
||||
sourceHandle: null,
|
||||
targetHandle: null,
|
||||
});
|
||||
|
||||
|
||||
// Verify the connect functions were called
|
||||
expect(sourceConnectSpy).toHaveBeenCalledWith(sourceNode, targetNode.id);
|
||||
expect(targetConnectSpy).toHaveBeenCalledWith(targetNode, sourceNode.id);
|
||||
|
||||
// Find this connection, and delete it
|
||||
const edge = useFlowStore.getState().edges[0];
|
||||
useFlowStore.getState().onEdgesDelete([edge]);
|
||||
|
||||
// Find the nodes in the flow
|
||||
const newSourceNode = useFlowStore.getState().nodes.find((node) => node.id == "source-1");
|
||||
const newTargetNode = useFlowStore.getState().nodes.find((node) => node.id == "target-1");
|
||||
|
||||
// Expect them to be the same after deleting the edges
|
||||
expect(newSourceNode).toBe(sourceNode);
|
||||
expect(newTargetNode).toBe(targetNode);
|
||||
|
||||
// Restore our spies
|
||||
sourceConnectSpy.mockRestore();
|
||||
targetConnectSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Reducing', () => {
|
||||
test.each(getAllTypes())('it should correctly call/ not call the reduce function when %s node is in a phase', (nodeType) => {
|
||||
// Create a phase node and a node of the current type
|
||||
|
||||
Reference in New Issue
Block a user