fix(editor): Fix schema view bugs (#14734)

Co-authored-by: Michael Kret <michael.k@radency.com>
This commit is contained in:
Elias Meire
2025-04-24 08:53:02 +02:00
committed by GitHub
parent 1b1d6043d6
commit 022f4755c2
14 changed files with 1040 additions and 781 deletions

View File

@@ -5,10 +5,9 @@ import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { createTestNode } from '@/__tests__/mocks';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { CUSTOM_API_CALL_KEY } from '@/constants';
vi.mock('@/stores/workflows.store', () => ({
useWorkflowsStore: vi.fn(),
}));
import { mockedStore } from '@/__tests__/utils';
import { mock } from 'vitest-mock-extended';
import type { ExecutionStatus, IRunData } from 'n8n-workflow';
describe('useNodeHelpers()', () => {
beforeAll(() => {
@@ -55,42 +54,8 @@ describe('useNodeHelpers()', () => {
expect(result).toEqual([]);
});
it('should return an empty array when workflowsStore.getWorkflowExecution() is null', () => {
vi.mocked(useWorkflowsStore).mockReturnValue({
getWorkflowExecution: null,
} as ReturnType<typeof useWorkflowsStore>);
const { getNodeInputData } = useNodeHelpers();
const node = createTestNode({
name: 'test',
type: 'test',
});
const result = getNodeInputData(node);
expect(result).toEqual([]);
});
it('should return an empty array when workflowsStore.getWorkflowExecution() is null', () => {
vi.mocked(useWorkflowsStore).mockReturnValue({
getWorkflowExecution: null,
} as ReturnType<typeof useWorkflowsStore>);
const { getNodeInputData } = useNodeHelpers();
const node = createTestNode({
name: 'test',
type: 'test',
});
const result = getNodeInputData(node);
expect(result).toEqual([]);
});
it('should return an empty array when resultData is not available', () => {
vi.mocked(useWorkflowsStore).mockReturnValue({
getWorkflowExecution: {
data: {
resultData: null,
},
},
} as unknown as ReturnType<typeof useWorkflowsStore>);
it('should return an empty array when runData is not available', () => {
mockedStore(useWorkflowsStore).getWorkflowRunData = null;
const { getNodeInputData } = useNodeHelpers();
const node = createTestNode({
name: 'test',
@@ -103,17 +68,9 @@ describe('useNodeHelpers()', () => {
it('should return an empty array when taskData is unavailable', () => {
const nodeName = 'Code';
vi.mocked(useWorkflowsStore).mockReturnValue({
getWorkflowExecution: {
data: {
resultData: {
runData: {
[nodeName]: [],
},
},
},
},
} as unknown as ReturnType<typeof useWorkflowsStore>);
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [],
});
const { getNodeInputData } = useNodeHelpers();
const node = createTestNode({
name: nodeName,
@@ -126,17 +83,9 @@ describe('useNodeHelpers()', () => {
it('should return an empty array when taskData.data is unavailable', () => {
const nodeName = 'Code';
vi.mocked(useWorkflowsStore).mockReturnValue({
getWorkflowExecution: {
data: {
resultData: {
runData: {
[nodeName]: [{ data: undefined }],
},
},
},
},
} as unknown as ReturnType<typeof useWorkflowsStore>);
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [{ data: undefined }],
});
const { getNodeInputData } = useNodeHelpers();
const node = createTestNode({
name: nodeName,
@@ -149,24 +98,16 @@ describe('useNodeHelpers()', () => {
it('should return input data from inputOverride', () => {
const nodeName = 'Code';
const data = { hello: 'world' };
vi.mocked(useWorkflowsStore).mockReturnValue({
getWorkflowExecution: {
data: {
resultData: {
runData: {
[nodeName]: [
{
inputOverride: {
main: [data],
},
},
],
},
const data = [{ json: { hello: 'world' } }];
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [
{
inputOverride: {
main: [data],
},
},
},
} as unknown as ReturnType<typeof useWorkflowsStore>);
],
});
const { getNodeInputData } = useNodeHelpers();
const node = createTestNode({
name: nodeName,
@@ -180,24 +121,10 @@ describe('useNodeHelpers()', () => {
it.each(['example', 'example.withdot', 'example.with.dots', 'example.with.dots and spaces'])(
'should return input data for "%s" node name, with given connection type and output index',
(nodeName) => {
const data = { hello: 'world' };
vi.mocked(useWorkflowsStore).mockReturnValue({
getWorkflowExecution: {
data: {
resultData: {
runData: {
[nodeName]: [
{
data: {
main: [data],
},
},
],
},
},
},
},
} as unknown as ReturnType<typeof useWorkflowsStore>);
const data = [{ json: { hello: 'world' } }];
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [{ data: { main: [data] } }],
});
const { getNodeInputData } = useNodeHelpers();
const node = createTestNode({
name: nodeName,
@@ -210,6 +137,92 @@ describe('useNodeHelpers()', () => {
);
});
describe('getLastRunIndexWithData()', () => {
const mockData = [{ json: { hello: 'world' } }];
it('should return the last runIndex with data', () => {
const nodeName = 'Test Node';
const { getLastRunIndexWithData } = useNodeHelpers();
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [{ data: { main: [mockData] } }, { data: { main: [mockData] } }],
});
expect(getLastRunIndexWithData(nodeName)).toEqual(1);
});
it('should return -1 when there are no runs', () => {
const nodeName = 'Test Node';
const { getLastRunIndexWithData } = useNodeHelpers();
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [],
});
expect(getLastRunIndexWithData(nodeName)).toEqual(-1);
});
it('should return -1 when there is no runData', () => {
const nodeName = 'Test Node';
const { getLastRunIndexWithData } = useNodeHelpers();
mockedStore(useWorkflowsStore).getWorkflowRunData = null;
expect(getLastRunIndexWithData(nodeName)).toEqual(-1);
});
it('should work with custom outputIndex', () => {
const nodeName = 'Test Node';
const { getLastRunIndexWithData } = useNodeHelpers();
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [
{ data: { main: [mockData, []] } },
{ data: { main: [mockData, []] } },
{ data: { main: [mockData, mockData] } },
{ data: { main: [[], mockData] } },
{ data: { main: [[], []] } },
],
});
expect(getLastRunIndexWithData(nodeName, 1)).toEqual(3);
});
it('should work with custom connectionType', () => {
const nodeName = 'Test Node';
const { getLastRunIndexWithData } = useNodeHelpers();
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [
{ data: { main: [mockData], ai_tool: [mockData] } },
{ data: { ai_tool: [mockData] } },
{ data: { main: [mockData] } },
],
});
expect(getLastRunIndexWithData(nodeName, 0, 'ai_tool')).toEqual(1);
});
});
describe('hasNodeExecuted()', () => {
it('should return false when runData is not available', () => {
const nodeName = 'Test Node';
mockedStore(useWorkflowsStore).getWorkflowRunData = null;
const { hasNodeExecuted } = useNodeHelpers();
expect(hasNodeExecuted(nodeName)).toBe(false);
});
it.each<{ status?: ExecutionStatus; expected: boolean }>([
{ status: undefined, expected: false },
{ status: 'waiting', expected: false },
{ status: 'running', expected: false },
{ status: 'error', expected: true },
{ status: 'success', expected: true },
])('should return $expected when execution status is $status', ({ status, expected }) => {
const nodeName = 'Test Node';
mockedStore(useWorkflowsStore).getWorkflowRunData = mock<IRunData>({
[nodeName]: [{ executionStatus: status }],
});
const { hasNodeExecuted } = useNodeHelpers();
expect(hasNodeExecuted(nodeName)).toBe(expected);
});
});
describe('assignNodeId()', () => {
it('should assign a unique id to the node', () => {
const { assignNodeId } = useNodeHelpers();