diff --git a/packages/editor-ui/src/components/RunDataSchema.vue b/packages/editor-ui/src/components/RunDataSchema.vue index a76012553f..50e5afd31f 100644 --- a/packages/editor-ui/src/components/RunDataSchema.vue +++ b/packages/editor-ui/src/components/RunDataSchema.vue @@ -42,6 +42,7 @@ type SchemaNode = { depth: number; loading: boolean; open: boolean; + connectedOutputIndexes: number[]; itemsCount: number | null; schema: Schema | null; }; @@ -94,6 +95,7 @@ const nodes = computed(() => { return { node: fullNode, + connectedOutputIndexes: node.indicies, depth: node.depth, itemsCount, nodeType, @@ -141,19 +143,17 @@ const highlight = computed(() => ndvStore.highlightDraggables); const allNodesOpen = computed(() => nodes.value.every((node) => node.open)); const noNodesOpen = computed(() => nodes.value.every((node) => !node.open)); -const loadNodeData = async (node: INodeUi) => { +const loadNodeData = async ({ node, connectedOutputIndexes }: SchemaNode) => { const pinData = workflowsStore.pinDataByNodeName(node.name); const data = pinData ?? - executionDataToJson( - getNodeInputData( - node, - props.runIndex, - props.outputIndex, - props.paneType, - props.connectionType, - ) ?? [], - ); + connectedOutputIndexes + .map((outputIndex) => + executionDataToJson( + getNodeInputData(node, props.runIndex, outputIndex, props.paneType, props.connectionType), + ), + ) + .flat(); nodesData.value[node.name] = { schema: getSchemaForExecutionData(data), @@ -161,7 +161,8 @@ const loadNodeData = async (node: INodeUi) => { }; }; -const toggleOpenNode = async ({ node, schema, open }: SchemaNode, exclusive = false) => { +const toggleOpenNode = async (schemaNode: SchemaNode, exclusive = false) => { + const { node, schema, open } = schemaNode; disableScrollInView.value = false; if (open) { nodesOpen.value[node.name] = false; @@ -170,7 +171,7 @@ const toggleOpenNode = async ({ node, schema, open }: SchemaNode, exclusive = fa if (!schema) { nodesLoading.value[node.name] = true; - await loadNodeData(node); + await loadNodeData(schemaNode); nodesLoading.value[node.name] = false; } @@ -182,8 +183,8 @@ const toggleOpenNode = async ({ node, schema, open }: SchemaNode, exclusive = fa }; const openAllNodes = async () => { - const nodesToLoad = nodes.value.filter((node) => !node.schema).map(({ node }) => node); - await Promise.all(nodesToLoad.map(async (node) => await loadNodeData(node))); + const nodesToLoad = nodes.value.filter((node) => !node.schema); + await Promise.all(nodesToLoad.map(loadNodeData)); nodesOpen.value = Object.fromEntries(nodes.value.map(({ node }) => [node.name, true])); }; diff --git a/packages/editor-ui/src/components/__tests__/RunDataSchema.test.ts b/packages/editor-ui/src/components/__tests__/RunDataSchema.test.ts index 8ac02d2152..5c8aedb36f 100644 --- a/packages/editor-ui/src/components/__tests__/RunDataSchema.test.ts +++ b/packages/editor-ui/src/components/__tests__/RunDataSchema.test.ts @@ -2,13 +2,19 @@ import { createComponentRenderer } from '@/__tests__/render'; import RunDataJsonSchema from '@/components/RunDataSchema.vue'; import { useWorkflowsStore } from '@/stores/workflows.store'; import { userEvent } from '@testing-library/user-event'; -import { cleanup, within } from '@testing-library/vue'; +import { cleanup, within, waitFor } from '@testing-library/vue'; import { createPinia, setActivePinia } from 'pinia'; -import { createTestNode, defaultNodeDescriptions } from '@/__tests__/mocks'; -import { SET_NODE_TYPE } from '@/constants'; +import { + createTestNode, + defaultNodeDescriptions, + mockNodeTypeDescription, +} from '@/__tests__/mocks'; +import { IF_NODE_TYPE, SET_NODE_TYPE } from '@/constants'; import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { mock } from 'vitest-mock-extended'; import type { IWorkflowDb } from '@/Interface'; +import { NodeConnectionType, type IDataObject } from 'n8n-workflow'; +import * as nodeHelpers from '@/composables/useNodeHelpers'; const mockNode1 = createTestNode({ name: 'Set1', @@ -31,13 +37,20 @@ const disabledNode = createTestNode({ disabled: true, }); +const ifNode = createTestNode({ + name: 'If', + type: IF_NODE_TYPE, + typeVersion: 1, + disabled: false, +}); + async function setupStore() { const workflow = mock({ id: '123', name: 'Test Workflow', connections: {}, active: true, - nodes: [mockNode1, mockNode2, disabledNode], + nodes: [mockNode1, mockNode2, disabledNode, ifNode], }); const pinia = createPinia(); @@ -46,12 +59,33 @@ async function setupStore() { const workflowsStore = useWorkflowsStore(); const nodeTypesStore = useNodeTypesStore(); - nodeTypesStore.setNodeTypes(defaultNodeDescriptions); + nodeTypesStore.setNodeTypes([ + ...defaultNodeDescriptions, + mockNodeTypeDescription({ + name: IF_NODE_TYPE, + outputs: [NodeConnectionType.Main, NodeConnectionType.Main], + }), + ]); workflowsStore.workflow = workflow; return pinia; } +function mockNodeOutputData(nodeName: string, data: IDataObject[], outputIndex = 0) { + const originalNodeHelpers = nodeHelpers.useNodeHelpers(); + vi.spyOn(nodeHelpers, 'useNodeHelpers').mockImplementation(() => { + return { + ...originalNodeHelpers, + getNodeInputData: vi.fn((node, _, output) => { + if (node.name === nodeName && output === outputIndex) { + return data.map((json) => ({ json })); + } + return []; + }), + }; + }); +} + describe('RunDataSchema.vue', () => { let renderComponent: ReturnType; @@ -122,7 +156,7 @@ describe('RunDataSchema.vue', () => { expect(within(nodes[1]).getByTestId('run-data-schema-node-schema')).toMatchSnapshot(); }); - it('renders schema for in output pane', async () => { + it('renders schema in output pane', async () => { const { container } = renderComponent({ props: { nodes: [], @@ -183,6 +217,28 @@ describe('RunDataSchema.vue', () => { ); }); + it('renders schema for correct output branch', async () => { + mockNodeOutputData( + 'If', + [ + { id: 1, name: 'John' }, + { id: 2, name: 'Jane' }, + ], + 1, + ); + const { getByTestId } = renderComponent({ + props: { + nodes: [{ name: 'If', indicies: [1], depth: 2 }], + }, + }); + + await waitFor(() => { + expect(getByTestId('run-data-schema-node-name')).toHaveTextContent('If'); + expect(getByTestId('run-data-schema-node-item-count')).toHaveTextContent('2 items'); + expect(getByTestId('run-data-schema-node-schema')).toMatchSnapshot(); + }); + }); + test.each([[[{ tx: false }, { tx: false }]], [[{ tx: '' }, { tx: '' }]], [[{ tx: [] }]]])( 'renders schema instead of showing no data for %o', (data) => { diff --git a/packages/editor-ui/src/components/__tests__/__snapshots__/RunDataSchema.test.ts.snap b/packages/editor-ui/src/components/__tests__/__snapshots__/RunDataSchema.test.ts.snap index 942eb55aa3..de6c683589 100644 --- a/packages/editor-ui/src/components/__tests__/__snapshots__/RunDataSchema.test.ts.snap +++ b/packages/editor-ui/src/components/__tests__/__snapshots__/RunDataSchema.test.ts.snap @@ -1,5 +1,1059 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`RunDataSchema.vue > renders schema for correct output branch 1`] = ` +
+
+
+
+ + +
+ + +
+
+ +
+
+
+ + + + + + + + name + + + + +
+ + + + + + + + + John + + + + + + +
+ + + +
+
+
+
+ + + + + + + + age + + + + +
+ + + + + + + + + 22 + + + + + + +
+ + + +
+
+
+
+ + + + + + + + hobbies + + + + +
+ +
+ + +
+
+ +
+
+
+ + + + + + + hobbies + + + + + + + + [0] + + + + +
+ + + + + + + + + surfing + + + + + + +
+ + + +
+
+
+
+ + + + + + + hobbies + + + + + + + + [1] + + + + +
+ + + + + + + + + traveling + + + + + + +
+ + + +
+ +
+
+
+ +
+
+
+
+
+`; + +exports[`RunDataSchema.vue > renders schema for correct output branch 2`] = ` +
+
+
+
+ + +
+ + +
+
+ +
+
+
+ + + + + + + + name + + + + +
+ + + + + + + + + John + + + + + + +
+ + + +
+
+
+
+ + + + + + + + age + + + + +
+ + + + + + + + + 22 + + + + + + +
+ + + +
+
+
+
+ + + + + + + + hobbies + + + + +
+ +
+ + +
+
+ +
+
+
+ + + + + + + hobbies + + + + + + + + [0] + + + + +
+ + + + + + + + + surfing + + + + + + +
+ + + +
+
+
+
+ + + + + + + hobbies + + + + + + + + [1] + + + + +
+ + + + + + + + + traveling + + + + + + +
+ + + +
+ +
+
+
+ +
+
+
+
+
+`; + +exports[`RunDataSchema.vue > renders schema for correct output branch 3`] = ` +
+
+
+
+ + +
+ + +
+
+ +
+
+
+ + + + + + + + id + + + + +
+ + + + + + + + + 1 + + + + + + +
+ + + +
+
+
+
+ + + + + + + + name + + + + +
+ + + + + + + + + John + + + + + + +
+ + + +
+ +
+
+
+
+
+`; + exports[`RunDataSchema.vue > renders schema for data 1`] = `
renders schema for in output pane 1`] = `
`; +exports[`RunDataSchema.vue > renders schema in output pane 1`] = ` +
+
+
+
+
+ + +
+ + +
+
+ +
+
+
+ + + + + + + + name + + + + +
+ + + + + + + + + John + + + + + + +
+ + + +
+
+
+
+ + + + + + + + age + + + + +
+ + + + + + + + + 22 + + + + + + +
+ + + +
+
+
+
+ + + + + + + + hobbies + + + + +
+ +
+ + +
+
+ +
+
+
+ + + + + + + hobbies + + + + + + + + [0] + + + + +
+ + + + + + + + + surfing + + + + + + +
+ + + +
+
+
+
+ + + + + + + hobbies + + + + + + + + [1] + + + + +
+ + + + + + + + + traveling + + + + + + +
+ + + +
+ +
+
+
+ +
+
+
+
+
+
+`; + exports[`RunDataSchema.vue > renders schema with spaces and dots 1`] = `