mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 08:34:07 -08:00
fix: Check if execution will wait on Test step click (no-changelog) (#11311)
# Conflicts: # packages/editor-ui/src/stores/workflows.store.ts
This commit is contained in:
parent
f52a771735
commit
1ae9b29758
|
@ -157,8 +157,9 @@ const getTriggerNodeTooltip = computed(() => {
|
|||
});
|
||||
|
||||
const isPollingTypeNode = computed(() => !!nodeType.value?.polling);
|
||||
|
||||
const isExecuting = computed(() => {
|
||||
if (!node.value) return false;
|
||||
if (!node.value || !workflowRunning.value) return false;
|
||||
return workflowsStore.isNodeExecuting(node.value.name);
|
||||
});
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ defineOptions({
|
|||
const lastPopupCountUpdate = ref(0);
|
||||
|
||||
const router = useRouter();
|
||||
const { runWorkflowResolvePending, stopCurrentExecution } = useRunWorkflow({ router });
|
||||
const { runWorkflow, runWorkflowResolvePending, stopCurrentExecution } = useRunWorkflow({ router });
|
||||
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const externalHooks = useExternalHooks();
|
||||
|
@ -76,10 +76,10 @@ const nodeType = computed((): INodeTypeDescription | null => {
|
|||
});
|
||||
|
||||
const isNodeRunning = computed(() => {
|
||||
if (!uiStore.isActionActive['workflowRunning']) return false;
|
||||
const triggeredNode = workflowsStore.executedNode;
|
||||
return (
|
||||
uiStore.isActionActive.workflowRunning &&
|
||||
(workflowsStore.isNodeExecuting(node.value?.name ?? '') || triggeredNode === node.value?.name)
|
||||
workflowsStore.isNodeExecuting(node.value?.name ?? '') || triggeredNode === node.value?.name
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -264,10 +264,18 @@ async function onClick() {
|
|||
telemetry.track('User clicked execute node button', telemetryPayload);
|
||||
await externalHooks.run('nodeExecuteButton.onClick', telemetryPayload);
|
||||
|
||||
await runWorkflowResolvePending({
|
||||
destinationNode: props.nodeName,
|
||||
source: 'RunData.ExecuteNodeButton',
|
||||
});
|
||||
if (workflowsStore.isWaitingExecution) {
|
||||
await runWorkflowResolvePending({
|
||||
destinationNode: props.nodeName,
|
||||
source: 'RunData.ExecuteNodeButton',
|
||||
});
|
||||
} else {
|
||||
await runWorkflow({
|
||||
destinationNode: props.nodeName,
|
||||
source: 'RunData.ExecuteNodeButton',
|
||||
});
|
||||
}
|
||||
|
||||
emit('execute');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ const defaultOutputMode = computed<OutputType>(() => {
|
|||
});
|
||||
|
||||
const isNodeRunning = computed(() => {
|
||||
return !!node.value && workflowsStore.isNodeExecuting(node.value.name);
|
||||
return workflowRunning.value && !!node.value && workflowsStore.isNodeExecuting(node.value.name);
|
||||
});
|
||||
|
||||
const workflowRunning = computed(() => {
|
||||
|
|
|
@ -378,6 +378,7 @@ export function useRunWorkflow(useRunWorkflowOpts: { router: ReturnType<typeof u
|
|||
['error', 'canceled', 'crashed', 'success'].includes(execution.status)
|
||||
) {
|
||||
workflowsStore.setWorkflowExecutionData(execution);
|
||||
uiStore.removeActiveAction('workflowRunning');
|
||||
workflowsStore.activeExecutionId = null;
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
resolve();
|
||||
|
|
|
@ -195,6 +195,7 @@ export const CHAIN_SUMMARIZATION_LANGCHAIN_NODE_TYPE =
|
|||
export const SIMULATE_NODE_TYPE = 'n8n-nodes-base.simulate';
|
||||
export const SIMULATE_TRIGGER_NODE_TYPE = 'n8n-nodes-base.simulateTrigger';
|
||||
export const AI_TRANSFORM_NODE_TYPE = 'n8n-nodes-base.aiTransform';
|
||||
export const FORM_NODE_TYPE = 'n8n-nodes-base.form';
|
||||
|
||||
export const CREDENTIAL_ONLY_NODE_PREFIX = 'n8n-creds-base';
|
||||
export const CREDENTIAL_ONLY_HTTP_NODE_VERSION = 4.1;
|
||||
|
|
|
@ -2,18 +2,33 @@ import { setActivePinia, createPinia } from 'pinia';
|
|||
import * as workflowsApi from '@/api/workflows';
|
||||
import {
|
||||
DUPLICATE_POSTFFIX,
|
||||
FORM_NODE_TYPE,
|
||||
MAX_WORKFLOW_NAME_LENGTH,
|
||||
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||
WAIT_NODE_TYPE,
|
||||
} from '@/constants';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import type { IExecutionResponse, INodeUi, IWorkflowDb, IWorkflowSettings } from '@/Interface';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import { type ExecutionSummary, type IConnection, type INodeExecutionData } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
SEND_AND_WAIT_OPERATION,
|
||||
type ExecutionSummary,
|
||||
type IConnection,
|
||||
type INodeExecutionData,
|
||||
} from 'n8n-workflow';
|
||||
import { stringSizeInBytes } from '@/utils/typesUtils';
|
||||
import { dataPinningEventBus } from '@/event-bus';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import type { PushPayload } from '@n8n/api-types';
|
||||
import { flushPromises } from '@vue/test-utils';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
|
||||
vi.mock('@/stores/ndv.store', () => ({
|
||||
useNDVStore: vi.fn(() => ({
|
||||
activeNode: null,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@/api/workflows', () => ({
|
||||
getWorkflows: vi.fn(),
|
||||
|
@ -49,6 +64,67 @@ describe('useWorkflowsStore', () => {
|
|||
expect(workflowsStore.workflow.id).toBe(PLACEHOLDER_EMPTY_WORKFLOW_ID);
|
||||
});
|
||||
|
||||
describe('isWaitingExecution', () => {
|
||||
it('should return false if no activeNode and no waiting nodes in workflow', () => {
|
||||
workflowsStore.workflow.nodes = [
|
||||
{ type: 'type1' },
|
||||
{ type: 'type2' },
|
||||
] as unknown as IWorkflowDb['nodes'];
|
||||
|
||||
const isWaiting = workflowsStore.isWaitingExecution;
|
||||
expect(isWaiting).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false if no activeNode and waiting node in workflow and waiting node is disabled', () => {
|
||||
workflowsStore.workflow.nodes = [
|
||||
{ type: FORM_NODE_TYPE, disabled: true },
|
||||
{ type: 'type2' },
|
||||
] as unknown as IWorkflowDb['nodes'];
|
||||
|
||||
const isWaiting = workflowsStore.isWaitingExecution;
|
||||
expect(isWaiting).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return true if no activeNode and wait node in workflow', () => {
|
||||
workflowsStore.workflow.nodes = [
|
||||
{ type: WAIT_NODE_TYPE },
|
||||
{ type: 'type2' },
|
||||
] as unknown as IWorkflowDb['nodes'];
|
||||
|
||||
const isWaiting = workflowsStore.isWaitingExecution;
|
||||
expect(isWaiting).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true if no activeNode and form node in workflow', () => {
|
||||
workflowsStore.workflow.nodes = [
|
||||
{ type: FORM_NODE_TYPE },
|
||||
{ type: 'type2' },
|
||||
] as unknown as IWorkflowDb['nodes'];
|
||||
|
||||
const isWaiting = workflowsStore.isWaitingExecution;
|
||||
expect(isWaiting).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true if no activeNode and sendAndWait node in workflow', () => {
|
||||
workflowsStore.workflow.nodes = [
|
||||
{ type: 'type1', parameters: { operation: SEND_AND_WAIT_OPERATION } },
|
||||
{ type: 'type2' },
|
||||
] as unknown as IWorkflowDb['nodes'];
|
||||
|
||||
const isWaiting = workflowsStore.isWaitingExecution;
|
||||
expect(isWaiting).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true if activeNode is waiting node', () => {
|
||||
vi.mocked(useNDVStore).mockReturnValue({
|
||||
activeNode: { type: WAIT_NODE_TYPE } as unknown as INodeUi,
|
||||
} as unknown as ReturnType<typeof useNDVStore>);
|
||||
|
||||
const isWaiting = workflowsStore.isWaitingExecution;
|
||||
expect(isWaiting).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allWorkflows', () => {
|
||||
it('should return sorted workflows by name', () => {
|
||||
workflowsStore.setWorkflows([
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
DEFAULT_NEW_WORKFLOW_NAME,
|
||||
DUPLICATE_POSTFFIX,
|
||||
ERROR_TRIGGER_NODE_TYPE,
|
||||
FORM_NODE_TYPE,
|
||||
MAX_WORKFLOW_NAME_LENGTH,
|
||||
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||
START_NODE_TYPE,
|
||||
|
@ -182,12 +183,46 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
|
||||
const allNodes = computed<INodeUi[]>(() => workflow.value.nodes);
|
||||
|
||||
const isWaitingExecution = computed(() => {
|
||||
return allNodes.value.some(
|
||||
(node) =>
|
||||
(node.type === WAIT_NODE_TYPE || node.parameters.operation === SEND_AND_WAIT_OPERATION) &&
|
||||
node.disabled !== true,
|
||||
const willNodeWait = (node: INodeUi): boolean => {
|
||||
return (
|
||||
(node.type === WAIT_NODE_TYPE ||
|
||||
node.type === FORM_NODE_TYPE ||
|
||||
node.parameters?.operation === SEND_AND_WAIT_OPERATION) &&
|
||||
node.disabled !== true
|
||||
);
|
||||
};
|
||||
|
||||
const isWaitingExecution = computed(() => {
|
||||
const activeNode = useNDVStore().activeNode;
|
||||
|
||||
if (activeNode) {
|
||||
if (willNodeWait(activeNode)) return true;
|
||||
|
||||
const workflow = getCurrentWorkflow();
|
||||
const parentNodes = workflow.getParentNodes(activeNode.name);
|
||||
|
||||
for (const parentNode of parentNodes) {
|
||||
if (willNodeWait(workflow.nodes[parentNode])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return allNodes.value.some((node) => willNodeWait(node));
|
||||
});
|
||||
|
||||
const isWorkflowRunning = computed(() => {
|
||||
if (uiStore.isActionActive.workflowRunning) return true;
|
||||
|
||||
if (activeExecutionId.value) {
|
||||
const execution = getWorkflowExecution;
|
||||
if (execution.value && execution.value.status === 'waiting' && !execution.value.finished) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Names of all nodes currently on canvas.
|
||||
|
@ -1614,6 +1649,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
|||
allConnections,
|
||||
allNodes,
|
||||
isWaitingExecution,
|
||||
isWorkflowRunning,
|
||||
canvasNames,
|
||||
nodesByName,
|
||||
nodesIssuesExist,
|
||||
|
|
|
@ -951,14 +951,7 @@ const projectPermissions = computed(() => {
|
|||
|
||||
const isStoppingExecution = ref(false);
|
||||
|
||||
const isWorkflowRunning = computed(() => {
|
||||
if (uiStore.isActionActive.workflowRunning) return true;
|
||||
if (workflowsStore.activeExecutionId) {
|
||||
const execution = workflowsStore.getWorkflowExecution;
|
||||
if (execution && execution.status === 'waiting' && !execution.finished) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
const isWorkflowRunning = computed(() => workflowsStore.isWorkflowRunning);
|
||||
const isExecutionWaitingForWebhook = computed(() => workflowsStore.executionWaitingForWebhook);
|
||||
|
||||
const isExecutionDisabled = computed(() => {
|
||||
|
|
|
@ -426,12 +426,7 @@ export default defineComponent({
|
|||
return this.workflowsStore.getWorkflowExecution;
|
||||
},
|
||||
workflowRunning(): boolean {
|
||||
if (this.uiStore.isActionActive.workflowRunning) return true;
|
||||
if (this.workflowsStore.activeExecutionId) {
|
||||
const execution = this.workflowsStore.getWorkflowExecution;
|
||||
if (execution && execution.status === 'waiting' && !execution.finished) return true;
|
||||
}
|
||||
return false;
|
||||
return this.workflowsStore.isWorkflowRunning;
|
||||
},
|
||||
currentWorkflow(): string {
|
||||
return this.$route.params.name?.toString() || this.workflowsStore.workflowId;
|
||||
|
|
Loading…
Reference in a new issue