mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Show correct options in the NDV runs selector (#19297)
Co-authored-by: Charlie Kolb <charlie@n8n.io>
This commit is contained in:
@@ -606,6 +606,128 @@ describe('RunData', () => {
|
|||||||
expect(getByTestId('ndv-items-count')).toBeInTheDocument();
|
expect(getByTestId('ndv-items-count')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('computed properties for branch handling', () => {
|
||||||
|
it('no run selector when no run data exists', () => {
|
||||||
|
const { container } = render({
|
||||||
|
displayMode: 'json',
|
||||||
|
runs: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Component instance checks would need to be done differently in Vue 3
|
||||||
|
// For now, we verify the behavior through the UI
|
||||||
|
expect(container.querySelector('.run-selector')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Show run selector when branch switch is shown (with all runs)', async () => {
|
||||||
|
// Create multiple runs with data in different outputs
|
||||||
|
const multipleRuns = [
|
||||||
|
{
|
||||||
|
startTime: Date.now(),
|
||||||
|
executionIndex: 0,
|
||||||
|
executionTime: 1,
|
||||||
|
data: {
|
||||||
|
main: [
|
||||||
|
[{ json: { value: 1 } }], // output 0
|
||||||
|
[{ json: { value: 2 } }], // output 1
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startTime: Date.now(),
|
||||||
|
executionIndex: 1,
|
||||||
|
executionTime: 1,
|
||||||
|
data: {
|
||||||
|
main: [
|
||||||
|
[{ json: { value: 3 } }], // output 0
|
||||||
|
[], // output 1 empty
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startTime: Date.now(),
|
||||||
|
executionIndex: 2,
|
||||||
|
executionTime: 1,
|
||||||
|
data: {
|
||||||
|
main: [
|
||||||
|
[], // output 0 empty
|
||||||
|
[{ json: { value: 4 } }], // output 1
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { getByTestId, findAllByTestId } = render({
|
||||||
|
displayMode: 'json',
|
||||||
|
runs: multipleRuns,
|
||||||
|
});
|
||||||
|
|
||||||
|
// When there are multiple branches and outputs, the run selector should be visible
|
||||||
|
const runSelector = getByTestId('run-selector');
|
||||||
|
expect(runSelector).toBeInTheDocument();
|
||||||
|
|
||||||
|
const runSelectorOptionsCount = await findAllByTestId('run-selection-option');
|
||||||
|
expect(runSelectorOptionsCount.length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Show run selector when there is no branch selector (only runs for branch with data)', async () => {
|
||||||
|
// Create multiple runs with data in different outputs
|
||||||
|
const multipleRuns = [
|
||||||
|
{
|
||||||
|
startTime: Date.now(),
|
||||||
|
executionIndex: 0,
|
||||||
|
executionTime: 1,
|
||||||
|
data: {
|
||||||
|
main: [
|
||||||
|
[{ json: { value: 1 } }], // output 0
|
||||||
|
[{ json: { value: 2 } }], // output 1
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startTime: Date.now(),
|
||||||
|
executionIndex: 1,
|
||||||
|
executionTime: 1,
|
||||||
|
data: {
|
||||||
|
main: [
|
||||||
|
[{ json: { value: 3 } }], // output 0
|
||||||
|
[], // output 1 empty
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startTime: Date.now(),
|
||||||
|
executionIndex: 2,
|
||||||
|
executionTime: 1,
|
||||||
|
data: {
|
||||||
|
main: [
|
||||||
|
[], // output 0 empty
|
||||||
|
[{ json: { value: 4 } }], // output 1
|
||||||
|
],
|
||||||
|
},
|
||||||
|
source: [null],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { getByTestId, findAllByTestId } = render({
|
||||||
|
displayMode: 'json',
|
||||||
|
runs: multipleRuns,
|
||||||
|
overrideOutputs: [1],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should show run selector since there are multiple runs
|
||||||
|
const runSelector = getByTestId('run-selector');
|
||||||
|
expect(runSelector).toBeInTheDocument();
|
||||||
|
|
||||||
|
const runSelectorOptionsCount = await findAllByTestId('run-selection-option');
|
||||||
|
expect(runSelectorOptionsCount.length).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Default values for the render function
|
// Default values for the render function
|
||||||
const nodes = [
|
const nodes = [
|
||||||
{
|
{
|
||||||
@@ -627,6 +749,7 @@ describe('RunData', () => {
|
|||||||
paneType = 'output',
|
paneType = 'output',
|
||||||
metadata,
|
metadata,
|
||||||
runs,
|
runs,
|
||||||
|
overrideOutputs,
|
||||||
}: {
|
}: {
|
||||||
defaultRunItems?: INodeExecutionData[];
|
defaultRunItems?: INodeExecutionData[];
|
||||||
workflowId?: string;
|
workflowId?: string;
|
||||||
@@ -636,6 +759,7 @@ describe('RunData', () => {
|
|||||||
paneType?: NodePanelType;
|
paneType?: NodePanelType;
|
||||||
metadata?: ITaskMetadata;
|
metadata?: ITaskMetadata;
|
||||||
runs?: ITaskData[];
|
runs?: ITaskData[];
|
||||||
|
overrideOutputs?: number[];
|
||||||
}) => {
|
}) => {
|
||||||
const defaultRun: ITaskData = {
|
const defaultRun: ITaskData = {
|
||||||
startTime: Date.now(),
|
startTime: Date.now(),
|
||||||
@@ -735,6 +859,7 @@ describe('RunData', () => {
|
|||||||
tooMuchDataTitle: '',
|
tooMuchDataTitle: '',
|
||||||
executingMessage: '',
|
executingMessage: '',
|
||||||
noDataInBranchMessage: '',
|
noDataInBranchMessage: '',
|
||||||
|
overrideOutputs,
|
||||||
},
|
},
|
||||||
pinia,
|
pinia,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -388,6 +388,9 @@ const maxOutputIndex = computed(() => {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
const currentPageOffset = computed(() => pageSize.value * (currentPage.value - 1));
|
const currentPageOffset = computed(() => pageSize.value * (currentPage.value - 1));
|
||||||
|
const showBranchSwitch = computed(
|
||||||
|
() => maxOutputIndex.value > 0 && branches.value.length > 1 && !displaysMultipleNodes.value,
|
||||||
|
);
|
||||||
const maxRunIndex = computed(() => {
|
const maxRunIndex = computed(() => {
|
||||||
if (!node.value) {
|
if (!node.value) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -406,6 +409,29 @@ const maxRunIndex = computed(() => {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const runSelectorOptionsCount = computed(() => {
|
||||||
|
if (!node.value) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const runData: IRunData | null = workflowRunData.value;
|
||||||
|
|
||||||
|
if (!runData?.hasOwnProperty(node.value.name)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is branch selector – we show all runs in the run selector
|
||||||
|
if (showBranchSwitch.value) {
|
||||||
|
return maxRunIndex.value + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is only one branch - we show only the runs containing the data in the connected branch
|
||||||
|
return runData[node.value.name].filter((nodeRun) => {
|
||||||
|
const nodeOutput = nodeRun?.data?.[connectionType.value]?.[currentOutputIndex.value];
|
||||||
|
return nodeOutput && nodeOutput?.length > 0;
|
||||||
|
}).length;
|
||||||
|
});
|
||||||
|
|
||||||
const rawInputData = computed(() =>
|
const rawInputData = computed(() =>
|
||||||
getRawInputData(props.runIndex, currentOutputIndex.value, connectionType.value),
|
getRawInputData(props.runIndex, currentOutputIndex.value, connectionType.value),
|
||||||
);
|
);
|
||||||
@@ -1136,7 +1162,7 @@ function getRunLabel(option: number) {
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
const itemsLabel = itemsCount > 0 ? ` (${items}${subexecutions})` : '';
|
const itemsLabel = itemsCount > 0 ? ` (${items}${subexecutions})` : '';
|
||||||
return option + i18n.baseText('ndv.output.of') + (maxRunIndex.value + 1) + itemsLabel;
|
return option + i18n.baseText('ndv.output.of') + runSelectorOptionsCount.value + itemsLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRawInputData(
|
function getRawInputData(
|
||||||
@@ -1510,10 +1536,11 @@ defineExpose({ enterEditMode });
|
|||||||
>
|
>
|
||||||
<template #prepend>{{ i18n.baseText('ndv.output.run') }}</template>
|
<template #prepend>{{ i18n.baseText('ndv.output.run') }}</template>
|
||||||
<N8nOption
|
<N8nOption
|
||||||
v-for="option in maxRunIndex + 1"
|
v-for="option in runSelectorOptionsCount"
|
||||||
:key="option"
|
:key="option"
|
||||||
:label="getRunLabel(option)"
|
:label="getRunLabel(option)"
|
||||||
:value="option - 1"
|
:value="option - 1"
|
||||||
|
data-test-id="run-selection-option"
|
||||||
></N8nOption>
|
></N8nOption>
|
||||||
</N8nSelect>
|
</N8nSelect>
|
||||||
|
|
||||||
@@ -1564,11 +1591,7 @@ defineExpose({ enterEditMode });
|
|||||||
<N8nText v-n8n-html="hint.message" size="small"></N8nText>
|
<N8nText v-n8n-html="hint.message" size="small"></N8nText>
|
||||||
</N8nCallout>
|
</N8nCallout>
|
||||||
|
|
||||||
<div
|
<div v-if="showBranchSwitch" :class="$style.outputs" data-test-id="branches">
|
||||||
v-if="maxOutputIndex > 0 && branches.length > 1 && !displaysMultipleNodes"
|
|
||||||
:class="$style.outputs"
|
|
||||||
data-test-id="branches"
|
|
||||||
>
|
|
||||||
<slot v-if="inputSelectLocation === 'outputs'" name="input-select"></slot>
|
<slot v-if="inputSelectLocation === 'outputs'" name="input-select"></slot>
|
||||||
<ViewSubExecution
|
<ViewSubExecution
|
||||||
v-if="activeTaskMetadata && !(paneType === 'input' && hasInputOverwrite)"
|
v-if="activeTaskMetadata && !(paneType === 'input' && hasInputOverwrite)"
|
||||||
|
|||||||
Reference in New Issue
Block a user