mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Use correct output for connected nodes in schema view (#10928)
This commit is contained in:
parent
a81256aff5
commit
ad60d49b42
|
@ -42,6 +42,7 @@ type SchemaNode = {
|
||||||
depth: number;
|
depth: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
connectedOutputIndexes: number[];
|
||||||
itemsCount: number | null;
|
itemsCount: number | null;
|
||||||
schema: Schema | null;
|
schema: Schema | null;
|
||||||
};
|
};
|
||||||
|
@ -94,6 +95,7 @@ const nodes = computed(() => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
node: fullNode,
|
node: fullNode,
|
||||||
|
connectedOutputIndexes: node.indicies,
|
||||||
depth: node.depth,
|
depth: node.depth,
|
||||||
itemsCount,
|
itemsCount,
|
||||||
nodeType,
|
nodeType,
|
||||||
|
@ -141,19 +143,17 @@ const highlight = computed(() => ndvStore.highlightDraggables);
|
||||||
const allNodesOpen = computed(() => nodes.value.every((node) => node.open));
|
const allNodesOpen = computed(() => nodes.value.every((node) => node.open));
|
||||||
const noNodesOpen = 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 pinData = workflowsStore.pinDataByNodeName(node.name);
|
||||||
const data =
|
const data =
|
||||||
pinData ??
|
pinData ??
|
||||||
|
connectedOutputIndexes
|
||||||
|
.map((outputIndex) =>
|
||||||
executionDataToJson(
|
executionDataToJson(
|
||||||
getNodeInputData(
|
getNodeInputData(node, props.runIndex, outputIndex, props.paneType, props.connectionType),
|
||||||
node,
|
),
|
||||||
props.runIndex,
|
)
|
||||||
props.outputIndex,
|
.flat();
|
||||||
props.paneType,
|
|
||||||
props.connectionType,
|
|
||||||
) ?? [],
|
|
||||||
);
|
|
||||||
|
|
||||||
nodesData.value[node.name] = {
|
nodesData.value[node.name] = {
|
||||||
schema: getSchemaForExecutionData(data),
|
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;
|
disableScrollInView.value = false;
|
||||||
if (open) {
|
if (open) {
|
||||||
nodesOpen.value[node.name] = false;
|
nodesOpen.value[node.name] = false;
|
||||||
|
@ -170,7 +171,7 @@ const toggleOpenNode = async ({ node, schema, open }: SchemaNode, exclusive = fa
|
||||||
|
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
nodesLoading.value[node.name] = true;
|
nodesLoading.value[node.name] = true;
|
||||||
await loadNodeData(node);
|
await loadNodeData(schemaNode);
|
||||||
nodesLoading.value[node.name] = false;
|
nodesLoading.value[node.name] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,8 +183,8 @@ const toggleOpenNode = async ({ node, schema, open }: SchemaNode, exclusive = fa
|
||||||
};
|
};
|
||||||
|
|
||||||
const openAllNodes = async () => {
|
const openAllNodes = async () => {
|
||||||
const nodesToLoad = nodes.value.filter((node) => !node.schema).map(({ node }) => node);
|
const nodesToLoad = nodes.value.filter((node) => !node.schema);
|
||||||
await Promise.all(nodesToLoad.map(async (node) => await loadNodeData(node)));
|
await Promise.all(nodesToLoad.map(loadNodeData));
|
||||||
nodesOpen.value = Object.fromEntries(nodes.value.map(({ node }) => [node.name, true]));
|
nodesOpen.value = Object.fromEntries(nodes.value.map(({ node }) => [node.name, true]));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,19 @@ import { createComponentRenderer } from '@/__tests__/render';
|
||||||
import RunDataJsonSchema from '@/components/RunDataSchema.vue';
|
import RunDataJsonSchema from '@/components/RunDataSchema.vue';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { userEvent } from '@testing-library/user-event';
|
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 { createPinia, setActivePinia } from 'pinia';
|
||||||
import { createTestNode, defaultNodeDescriptions } from '@/__tests__/mocks';
|
import {
|
||||||
import { SET_NODE_TYPE } from '@/constants';
|
createTestNode,
|
||||||
|
defaultNodeDescriptions,
|
||||||
|
mockNodeTypeDescription,
|
||||||
|
} from '@/__tests__/mocks';
|
||||||
|
import { IF_NODE_TYPE, SET_NODE_TYPE } from '@/constants';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { mock } from 'vitest-mock-extended';
|
import { mock } from 'vitest-mock-extended';
|
||||||
import type { IWorkflowDb } from '@/Interface';
|
import type { IWorkflowDb } from '@/Interface';
|
||||||
|
import { NodeConnectionType, type IDataObject } from 'n8n-workflow';
|
||||||
|
import * as nodeHelpers from '@/composables/useNodeHelpers';
|
||||||
|
|
||||||
const mockNode1 = createTestNode({
|
const mockNode1 = createTestNode({
|
||||||
name: 'Set1',
|
name: 'Set1',
|
||||||
|
@ -31,13 +37,20 @@ const disabledNode = createTestNode({
|
||||||
disabled: true,
|
disabled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ifNode = createTestNode({
|
||||||
|
name: 'If',
|
||||||
|
type: IF_NODE_TYPE,
|
||||||
|
typeVersion: 1,
|
||||||
|
disabled: false,
|
||||||
|
});
|
||||||
|
|
||||||
async function setupStore() {
|
async function setupStore() {
|
||||||
const workflow = mock<IWorkflowDb>({
|
const workflow = mock<IWorkflowDb>({
|
||||||
id: '123',
|
id: '123',
|
||||||
name: 'Test Workflow',
|
name: 'Test Workflow',
|
||||||
connections: {},
|
connections: {},
|
||||||
active: true,
|
active: true,
|
||||||
nodes: [mockNode1, mockNode2, disabledNode],
|
nodes: [mockNode1, mockNode2, disabledNode, ifNode],
|
||||||
});
|
});
|
||||||
|
|
||||||
const pinia = createPinia();
|
const pinia = createPinia();
|
||||||
|
@ -46,12 +59,33 @@ async function setupStore() {
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
const nodeTypesStore = useNodeTypesStore();
|
||||||
|
|
||||||
nodeTypesStore.setNodeTypes(defaultNodeDescriptions);
|
nodeTypesStore.setNodeTypes([
|
||||||
|
...defaultNodeDescriptions,
|
||||||
|
mockNodeTypeDescription({
|
||||||
|
name: IF_NODE_TYPE,
|
||||||
|
outputs: [NodeConnectionType.Main, NodeConnectionType.Main],
|
||||||
|
}),
|
||||||
|
]);
|
||||||
workflowsStore.workflow = workflow;
|
workflowsStore.workflow = workflow;
|
||||||
|
|
||||||
return pinia;
|
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', () => {
|
describe('RunDataSchema.vue', () => {
|
||||||
let renderComponent: ReturnType<typeof createComponentRenderer>;
|
let renderComponent: ReturnType<typeof createComponentRenderer>;
|
||||||
|
|
||||||
|
@ -122,7 +156,7 @@ describe('RunDataSchema.vue', () => {
|
||||||
expect(within(nodes[1]).getByTestId('run-data-schema-node-schema')).toMatchSnapshot();
|
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({
|
const { container } = renderComponent({
|
||||||
props: {
|
props: {
|
||||||
nodes: [],
|
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: [] }]]])(
|
test.each([[[{ tx: false }, { tx: false }]], [[{ tx: '' }, { tx: '' }]], [[{ tx: [] }]]])(
|
||||||
'renders schema instead of showing no data for %o',
|
'renders schema instead of showing no data for %o',
|
||||||
(data) => {
|
(data) => {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue