Skip to content

Commit

Permalink
Write tests for new FlowLogic.updateFlowNode() method
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshuaCWebDeveloper committed Apr 24, 2024
1 parent e791a37 commit f79ba79
Showing 1 changed file with 239 additions and 55 deletions.
294 changes: 239 additions & 55 deletions packages/flow-client/src/app/redux/modules/flow/flow.logic.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import '../../../../../vitest-esbuild-compat';
import { MockedFunction } from 'vitest';
import { RootState } from '../../store';
import { NodeEntity, selectAllNodes } from '../node/node.slice';
import { NodeEntity, selectAllNodes, selectNodeById } from '../node/node.slice';
import { FlowLogic, NodeModel, SerializedGraph } from './flow.logic';
import {
FlowEntity,
Expand All @@ -18,6 +18,7 @@ vi.mock('../node/node.slice', async importOriginal => {
return {
...originalModule,
selectAllNodes: vi.fn(() => []),
selectNodeById: vi.fn(() => null),
};
});

Expand All @@ -39,6 +40,9 @@ const mockGetState = vi.fn(() => ({})) as unknown as () => RootState;
const mockedSelectAllNodes = selectAllNodes as MockedFunction<
typeof selectAllNodes
>;
const mockedSelectNodeById = selectNodeById as MockedFunction<
typeof selectNodeById
>;
const mockedSelectEntityById = selectEntityById as MockedFunction<
typeof selectEntityById
>;
Expand Down Expand Up @@ -76,6 +80,82 @@ describe('flow.logic', () => {
flowLogic = new FlowLogic();
});

describe('getNodeInputsOutputs', () => {
const baseNodeProps = {
id: 'test-node',
nodeRedId: 'test-node',
module: 'module',
version: 'version',
name: 'name',
type: 'type',
};

it('should extract inputs and outputs with default labels when no custom labels are provided', () => {
const entity = {
...baseNodeProps,
id: 'test-node',
};

const instance = {
inputs: 2,
outputs: 1,
} as FlowNodeEntity;

const { inputs, outputs } = flowLogic.getNodeInputsOutputs(
instance,
entity
);

expect(inputs).toEqual(['Input 1', 'Input 2']);
expect(outputs).toEqual(['Output 1']);
});

it('should correctly deserialize and use custom input and output label functions', () => {
const entity = {
...baseNodeProps,
id: 'test-node',
definitionScript: `
RED.nodes.registerType("test-node", {
inputLabels: function(index) {
return \`Custom Input \${index + 1}\`;
},
outputLabels: function(index) {
return \`Custom Output \${index + 1}\`;
}
});
`,
};

const instance = {
inputs: 2,
outputs: 2,
} as FlowNodeEntity;

const { inputs, outputs } = flowLogic.getNodeInputsOutputs(
instance,
entity
);

expect(inputs).toEqual(['Custom Input 1', 'Custom Input 2']);
expect(outputs).toEqual(['Custom Output 1', 'Custom Output 2']);
});

it('should handle nodes without inputs or outputs', () => {
const node = {
...baseNodeProps,
id: 'test-node',
};

const { inputs, outputs } = flowLogic.getNodeInputsOutputs(
{} as FlowNodeEntity,
node
);

expect(inputs).toEqual([]);
expect(outputs).toEqual([]);
});
});

describe('updateFlowFromSerializedGraph', () => {
it('correctly creates a flow from a serialized graph', async () => {
const serializedGraph = {
Expand Down Expand Up @@ -279,79 +359,183 @@ describe('flow.logic', () => {
});
});

describe('getNodeInputsOutputs', () => {
const baseNodeProps = {
id: 'test-node',
nodeRedId: 'test-node',
module: 'module',
version: 'version',
name: 'name',
type: 'type',
describe('updateFlowNode', () => {
const testNodeEntity: NodeEntity = {
id: 'node1',
type: 'custom-node',
nodeRedId: 'node1',
name: 'Test Node',
module: 'test-module',
version: '1.0.0',
};

it('should extract inputs and outputs with default labels when no custom labels are provided', () => {
const entity = {
...baseNodeProps,
id: 'test-node',
};
const numInputs = 1;
const numOutputs = 2;

const testFlowNodeEntity: FlowNodeEntity = {
id: 'node1',
type: 'custom-node',
x: 100,
y: 200,
z: 'flow1',
name: 'Test Node',
wires: Array.from({ length: numOutputs }, () => []), // Assuming 1 output, no connections yet
inPorts: Array.from({ length: numInputs }, (_, i) => ({
id: `in${i}`,
type: 'default',
x: 0,
y: 0,
name: `Input Port ${i}`,
alignment: 'left',
maximumLinks: 1,
connected: false,
parentNode: 'node1',
links: [],
in: true,
extras: {
label: `Input Port ${i}`,
},
})), // 1 input port
outPorts: Array.from({ length: numOutputs }, (_, i) => ({
id: `out${i}`,
type: 'default',
x: 0,
y: 0,
name: `Output Port ${i}`,
alignment: 'right',
maximumLinks: 1,
connected: false,
parentNode: 'node1',
links: [],
in: false,
extras: {
label: `Output Port ${i}`,
},
})), // 1 output port
links: {},
inputs: numInputs,
outputs: numOutputs,
};
beforeEach(() => {
mockedSelectEntityById.mockImplementation((state, id) => {
if (id === 'node1') {
return testFlowNodeEntity;
}
return null as unknown as FlowNodeEntity;
});

const instance = {
inputs: 2,
outputs: 1,
} as FlowNodeEntity;
mockedSelectNodeById.mockImplementation((state, id) => {
if (id === 'custom-node') {
return testNodeEntity;
}
return null as unknown as NodeEntity;
});
});

const { inputs, outputs } = flowLogic.getNodeInputsOutputs(
instance,
entity
it('updates node inputs and outputs correctly', async () => {
const changes = {
inputs: 0,
outputs: 3,
};

await flowLogic.updateFlowNode('node1', changes)(
mockDispatch,
mockGetState
);

expect(inputs).toEqual(['Input 1', 'Input 2']);
expect(outputs).toEqual(['Output 1']);
expect(mockDispatch).toHaveBeenCalledWith(
flowActions.updateEntity({
id: 'node1',
changes: expect.objectContaining({
inputs: 0,
outputs: 3,
// Additional checks for ports and wires if necessary
}),
})
);
});

it('should correctly deserialize and use custom input and output label functions', () => {
const entity = {
...baseNodeProps,
id: 'test-node',
definitionScript: `
RED.nodes.registerType("test-node", {
inputLabels: function(index) {
return \`Custom Input \${index + 1}\`;
},
outputLabels: function(index) {
return \`Custom Output \${index + 1}\`;
}
});
`,
it('handles changes in node outputs correctly', async () => {
const changes = {
outputs: '{"0": "-1", "1": "0"}', // Move output 1 to 0, remove output 0
};

const instance = {
inputs: 2,
outputs: 2,
} as FlowNodeEntity;
await flowLogic.updateFlowNode('node1', changes)(
mockDispatch,
mockGetState
);

const { inputs, outputs } = flowLogic.getNodeInputsOutputs(
instance,
entity
expect(mockDispatch).toHaveBeenCalledWith(
flowActions.updateEntity({
id: 'node1',
changes: expect.objectContaining({
outputs: 1,
wires: [[]],
outPorts: expect.arrayContaining([
expect.objectContaining({
id: 'out1',
links: [],
}),
]),
// Verify that the output ports and wires are correctly updated
}),
})
);
});

expect(inputs).toEqual(['Custom Input 1', 'Custom Input 2']);
expect(outputs).toEqual(['Custom Output 1', 'Custom Output 2']);
it('updates node labels based on inputs and outputs', async () => {
const changes = {
inputs: 1,
outputs: 1,
};

await flowLogic.updateFlowNode('node1', changes)(
mockDispatch,
mockGetState
);

// Assuming the getNodeInputsOutputs method generates labels "Input 1" and "Output 1"
expect(mockDispatch).toHaveBeenCalledWith(
flowActions.updateEntity({
id: 'node1',
changes: expect.objectContaining({
inPorts: expect.arrayContaining([
expect.objectContaining({
extras: expect.objectContaining({
label: 'Input 1',
}),
}),
]),
outPorts: expect.arrayContaining([
expect.objectContaining({
extras: expect.objectContaining({
label: 'Output 1',
}),
}),
]),
}),
})
);
});

it('should handle nodes without inputs or outputs', () => {
const node = {
...baseNodeProps,
id: 'test-node',
it('removes all input ports when inputs set to 0', async () => {
const changes = {
inputs: 0, // Set inputs to 0, expecting all input ports to be removed
};

const { inputs, outputs } = flowLogic.getNodeInputsOutputs(
{} as FlowNodeEntity,
node
await flowLogic.updateFlowNode('node1', changes)(
mockDispatch,
mockGetState
);

expect(inputs).toEqual([]);
expect(outputs).toEqual([]);
expect(mockDispatch).toHaveBeenCalledWith(
flowActions.updateEntity({
id: 'node1',
changes: expect.objectContaining({
inPorts: [], // Expecting no input ports
}),
})
);
});
});

Expand Down

0 comments on commit f79ba79

Please sign in to comment.