mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Change label for unexecuted nodes (#14260)
This commit is contained in:
@@ -251,6 +251,8 @@ If that gets executed in one of the package folders it will only run the tests
|
|||||||
of this package. If it gets executed in the n8n-root folder it will run all
|
of this package. If it gets executed in the n8n-root folder it will run all
|
||||||
tests of all packages.
|
tests of all packages.
|
||||||
|
|
||||||
|
If you made a change which requires an update on a `.test.ts.snap` file, pass `-u` to the command to run tests or press `u` in watch mode.
|
||||||
|
|
||||||
#### E2E tests
|
#### E2E tests
|
||||||
|
|
||||||
⚠️ You have to run `pnpm cypress:install` to install cypress before running the tests for the first time and to update cypress.
|
⚠️ You have to run `pnpm cypress:install` to install cypress before running the tests for the first time and to update cypress.
|
||||||
|
|||||||
@@ -191,16 +191,14 @@ describe('VirtualSchema.vue', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders schema for empty data', async () => {
|
it('renders schema for empty data for unexecuted nodes', async () => {
|
||||||
const { getAllByText, getAllByTestId } = renderComponent();
|
const { getAllByText, getAllByTestId } = renderComponent();
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() => expect(getAllByText('Execute previous nodes').length).toBe(2));
|
||||||
expect(getAllByText("No fields - item(s) exist, but they're empty").length).toBe(2),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Collapse second node
|
// Collapse second node
|
||||||
await userEvent.click(getAllByTestId('run-data-schema-header')[1]);
|
await userEvent.click(getAllByTestId('run-data-schema-header')[1]);
|
||||||
expect(getAllByText("No fields - item(s) exist, but they're empty").length).toBe(1);
|
expect(getAllByText('Execute previous nodes').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders schema for empty data with binary', async () => {
|
it('renders schema for empty data with binary', async () => {
|
||||||
@@ -295,7 +293,7 @@ describe('VirtualSchema.vue', () => {
|
|||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const items = getAllByTestId('run-data-schema-item');
|
const items = getAllByTestId('run-data-schema-item');
|
||||||
expect(items).toHaveLength(6);
|
expect(items).toHaveLength(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
@@ -309,7 +307,7 @@ describe('VirtualSchema.vue', () => {
|
|||||||
|
|
||||||
const { getAllByText } = renderComponent();
|
const { getAllByText } = renderComponent();
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(getAllByText("No fields - item(s) exist, but they're empty").length).toBe(2),
|
expect(getAllByText("No fields - item(s) exist, but they're empty").length).toBe(1),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -482,7 +480,7 @@ describe('VirtualSchema.vue', () => {
|
|||||||
const { getAllByTestId } = renderComponent();
|
const { getAllByTestId } = renderComponent();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(getAllByTestId('run-data-schema-item')).toHaveLength(6);
|
expect(getAllByTestId('run-data-schema-item')).toHaveLength(5);
|
||||||
});
|
});
|
||||||
const items = getAllByTestId('run-data-schema-item');
|
const items = getAllByTestId('run-data-schema-item');
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import { asyncComputed } from '@vueuse/core';
|
|||||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
|
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
|
||||||
import { pick } from 'lodash-es';
|
import { pick } from 'lodash-es';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import NodeExecuteButton from './NodeExecuteButton.vue';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
nodes?: IConnectedNode[];
|
nodes?: IConnectedNode[];
|
||||||
@@ -89,7 +90,7 @@ const { getSchemaForExecutionData, getSchemaForJsonSchema, getSchema, filterSche
|
|||||||
useDataSchema();
|
useDataSchema();
|
||||||
const { closedNodes, flattenSchema, flattenMultipleSchemas, toggleLeaf, toggleNode } =
|
const { closedNodes, flattenSchema, flattenMultipleSchemas, toggleLeaf, toggleNode } =
|
||||||
useFlattenSchema();
|
useFlattenSchema();
|
||||||
const { getNodeInputData } = useNodeHelpers();
|
const { getNodeInputData, getNodeTaskData } = useNodeHelpers();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'clear:search': [];
|
'clear:search': [];
|
||||||
@@ -104,6 +105,8 @@ const toggleNodeAndScrollTop = (id: string) => {
|
|||||||
|
|
||||||
const getNodeSchema = async (fullNode: INodeUi, connectedNode: IConnectedNode) => {
|
const getNodeSchema = async (fullNode: INodeUi, connectedNode: IConnectedNode) => {
|
||||||
const pinData = workflowsStore.pinDataByNodeName(connectedNode.name);
|
const pinData = workflowsStore.pinDataByNodeName(connectedNode.name);
|
||||||
|
const hasPinnedData = pinData ? pinData.length > 0 : false;
|
||||||
|
const isNodeExecuted = getNodeTaskData(fullNode, props.runIndex) !== null || hasPinnedData;
|
||||||
const connectedOutputIndexes = connectedNode.indicies.length > 0 ? connectedNode.indicies : [0];
|
const connectedOutputIndexes = connectedNode.indicies.length > 0 ? connectedNode.indicies : [0];
|
||||||
const nodeData = connectedOutputIndexes.map((outputIndex) =>
|
const nodeData = connectedOutputIndexes.map((outputIndex) =>
|
||||||
getNodeInputData(fullNode, props.runIndex, outputIndex, props.paneType, props.connectionType),
|
getNodeInputData(fullNode, props.runIndex, outputIndex, props.paneType, props.connectionType),
|
||||||
@@ -128,6 +131,7 @@ const getNodeSchema = async (fullNode: INodeUi, connectedNode: IConnectedNode) =
|
|||||||
itemsCount: data.length,
|
itemsCount: data.length,
|
||||||
preview,
|
preview,
|
||||||
hasBinary,
|
hasBinary,
|
||||||
|
isNodeExecuted,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -242,10 +246,8 @@ const nodesSchemas = asyncComputed<SchemaNode[]>(async () => {
|
|||||||
const nodeType = nodeTypesStore.getNodeType(fullNode.type, fullNode.typeVersion);
|
const nodeType = nodeTypesStore.getNodeType(fullNode.type, fullNode.typeVersion);
|
||||||
if (!nodeType) continue;
|
if (!nodeType) continue;
|
||||||
|
|
||||||
const { schema, connectedOutputIndexes, itemsCount, preview, hasBinary } = await getNodeSchema(
|
const { schema, connectedOutputIndexes, itemsCount, preview, hasBinary, isNodeExecuted } =
|
||||||
fullNode,
|
await getNodeSchema(fullNode, node);
|
||||||
node,
|
|
||||||
);
|
|
||||||
|
|
||||||
const filteredSchema = filterSchema(schema, search);
|
const filteredSchema = filterSchema(schema, search);
|
||||||
|
|
||||||
@@ -260,6 +262,7 @@ const nodesSchemas = asyncComputed<SchemaNode[]>(async () => {
|
|||||||
schema: filteredSchema,
|
schema: filteredSchema,
|
||||||
preview,
|
preview,
|
||||||
hasBinary,
|
hasBinary,
|
||||||
|
isNodeExecuted,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,6 +434,34 @@ const onDragEnd = (el: HTMLElement) => {
|
|||||||
class="notice"
|
class="notice"
|
||||||
:style="{ marginLeft: `calc(var(--spacing-l) + var(--spacing-l) * ${item.level})` }"
|
:style="{ marginLeft: `calc(var(--spacing-l) + var(--spacing-l) * ${item.level})` }"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-else-if="item.type === 'empty'"
|
||||||
|
:style="{
|
||||||
|
paddingBottom: `var(--spacing-xs)`,
|
||||||
|
marginLeft: `var(--spacing-xl)`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<N8nText tag="div" size="small">
|
||||||
|
<i18n-t
|
||||||
|
v-if="item.key === 'executeSchema'"
|
||||||
|
tag="span"
|
||||||
|
keypath="dataMapping.schemaView.executeSchema"
|
||||||
|
>
|
||||||
|
<template #link>
|
||||||
|
<NodeExecuteButton
|
||||||
|
:node-name="item.nodeName"
|
||||||
|
text
|
||||||
|
telemetry-source="inputs"
|
||||||
|
hide-icon
|
||||||
|
:label="i18n.baseText('ndv.input.noOutputData.executePrevious')"
|
||||||
|
size="small"
|
||||||
|
:style="{ padding: 0 }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
<i18n-t v-else tag="span" :keypath="`dataMapping.schemaView.${item.key}`" />
|
||||||
|
</N8nText>
|
||||||
|
</div>
|
||||||
</DynamicScrollerItem>
|
</DynamicScrollerItem>
|
||||||
</template>
|
</template>
|
||||||
</DynamicScroller>
|
</DynamicScroller>
|
||||||
|
|||||||
@@ -2005,32 +2005,40 @@ exports[`VirtualSchema.vue > renders schema with spaces and dots 1`] = `
|
|||||||
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="schema-item draggable"
|
|
||||||
data-test-id="run-data-schema-item"
|
|
||||||
data-v-0f5e7239=""
|
|
||||||
data-v-d00cba9a=""
|
data-v-d00cba9a=""
|
||||||
type="item"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="toggle-container"
|
class="n8n-text size-small regular"
|
||||||
data-v-0f5e7239=""
|
data-v-d00cba9a=""
|
||||||
>
|
|
||||||
<!--v-if-->
|
|
||||||
</div>
|
|
||||||
<!--v-if-->
|
|
||||||
<!--v-if-->
|
|
||||||
<span
|
|
||||||
class="content text"
|
|
||||||
data-test-id="run-data-schema-item-value"
|
|
||||||
data-v-0f5e7239=""
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<span>
|
<span
|
||||||
<!--v-if-->
|
data-v-d00cba9a=""
|
||||||
No fields - item(s) exist, but they're empty
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
aria-live="polite"
|
||||||
|
class="button button primary small text el-tooltip__trigger el-tooltip__trigger"
|
||||||
|
style="padding: 0px;"
|
||||||
|
title="Runs the current node. Will also run previous nodes if they have not been run yet"
|
||||||
|
transparent-background="false"
|
||||||
|
>
|
||||||
|
<!--v-if-->
|
||||||
|
<span>
|
||||||
|
|
||||||
|
Execute previous nodes
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<!--teleport start-->
|
||||||
|
<!--teleport end-->
|
||||||
|
|
||||||
|
|
||||||
|
to see schema
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,7 @@ export type SchemaNode = {
|
|||||||
itemsCount: number;
|
itemsCount: number;
|
||||||
schema: Schema;
|
schema: Schema;
|
||||||
preview: boolean;
|
preview: boolean;
|
||||||
|
isNodeExecuted: boolean;
|
||||||
hasBinary: boolean;
|
hasBinary: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,7 +280,14 @@ export type RenderNotice = {
|
|||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Renders = RenderHeader | RenderItem | RenderIcon | RenderNotice;
|
export type RenderEmpty = {
|
||||||
|
id: string;
|
||||||
|
type: 'empty';
|
||||||
|
nodeName: string;
|
||||||
|
key: 'emptyData' | 'emptyDataWithBinary' | 'executeSchema';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Renders = RenderHeader | RenderItem | RenderIcon | RenderNotice | RenderEmpty;
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
object: 'cube',
|
object: 'cube',
|
||||||
@@ -296,13 +304,11 @@ const icons = {
|
|||||||
|
|
||||||
const getIconBySchemaType = (type: Schema['type']): string => icons[type];
|
const getIconBySchemaType = (type: Schema['type']): string => icons[type];
|
||||||
|
|
||||||
const emptyItem = (
|
const emptyItem = (key: RenderEmpty['key'], nodeName?: string): RenderEmpty => ({
|
||||||
message = useI18n().baseText('dataMapping.schemaView.emptyData'),
|
|
||||||
): RenderItem => ({
|
|
||||||
id: `empty-${window.crypto.randomUUID()}`,
|
id: `empty-${window.crypto.randomUUID()}`,
|
||||||
icon: '',
|
type: 'empty',
|
||||||
value: message,
|
key,
|
||||||
type: 'item',
|
nodeName: nodeName || '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const moreFieldsItem = (): RenderIcon => ({
|
const moreFieldsItem = (): RenderIcon => ({
|
||||||
@@ -361,10 +367,10 @@ export const useFlattenSchema = () => {
|
|||||||
prefix?: string;
|
prefix?: string;
|
||||||
level?: number;
|
level?: number;
|
||||||
preview?: boolean;
|
preview?: boolean;
|
||||||
}): RenderItem[] => {
|
}): Renders[] => {
|
||||||
// only show empty item for the first level
|
// only show empty item for the first level
|
||||||
if (isDataEmpty(schema) && depth <= 0) {
|
if (isDataEmpty(schema) && depth <= 0) {
|
||||||
return [emptyItem()];
|
return [emptyItem('emptyData')];
|
||||||
}
|
}
|
||||||
|
|
||||||
const expression = `{{ ${expressionPrefix ? expressionPrefix + schema.path : schema.path.slice(1)} }}`;
|
const expression = `{{ ${expressionPrefix ? expressionPrefix + schema.path : schema.path.slice(1)} }}`;
|
||||||
@@ -372,7 +378,7 @@ export const useFlattenSchema = () => {
|
|||||||
const id = expression;
|
const id = expression;
|
||||||
|
|
||||||
if (Array.isArray(schema.value)) {
|
if (Array.isArray(schema.value)) {
|
||||||
const items: RenderItem[] = [];
|
const items: Renders[] = [];
|
||||||
|
|
||||||
if (schema.key) {
|
if (schema.key) {
|
||||||
items.push({
|
items.push({
|
||||||
@@ -459,13 +465,14 @@ export const useFlattenSchema = () => {
|
|||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDataEmpty(item.schema) && !item.isNodeExecuted && !item.hasBinary) {
|
||||||
|
acc.push(emptyItem('executeSchema', item.node.name));
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
if (isDataEmpty(item.schema)) {
|
if (isDataEmpty(item.schema)) {
|
||||||
const message = useI18n().baseText(
|
const key = item.hasBinary ? 'emptyDataWithBinary' : 'emptyData';
|
||||||
item.hasBinary
|
acc.push(emptyItem(key));
|
||||||
? 'dataMapping.schemaView.emptyDataWithBinary'
|
|
||||||
: 'dataMapping.schemaView.emptyData',
|
|
||||||
);
|
|
||||||
acc.push(emptyItem(message));
|
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -669,6 +669,7 @@
|
|||||||
"dataMapping.tableView.tableColumnsExceeded.tooltip.link": "JSON view",
|
"dataMapping.tableView.tableColumnsExceeded.tooltip.link": "JSON view",
|
||||||
"dataMapping.schemaView.emptyData": "No fields - item(s) exist, but they're empty",
|
"dataMapping.schemaView.emptyData": "No fields - item(s) exist, but they're empty",
|
||||||
"dataMapping.schemaView.emptyDataWithBinary": "Only binary data exists. View it using the 'Binary' tab",
|
"dataMapping.schemaView.emptyDataWithBinary": "Only binary data exists. View it using the 'Binary' tab",
|
||||||
|
"dataMapping.schemaView.executeSchema": "{link} to see schema",
|
||||||
"dataMapping.schemaView.disabled": "This node is disabled and will just pass data through",
|
"dataMapping.schemaView.disabled": "This node is disabled and will just pass data through",
|
||||||
"dataMapping.schemaView.noMatches": "No results for '{search}'",
|
"dataMapping.schemaView.noMatches": "No results for '{search}'",
|
||||||
"dataMapping.schemaView.preview": "Usually outputs the following fields. Execute the node to see the actual ones. {link}",
|
"dataMapping.schemaView.preview": "Usually outputs the following fields. Execute the node to see the actual ones. {link}",
|
||||||
|
|||||||
Reference in New Issue
Block a user