This commit is contained in:
Michael Kret 2024-11-11 13:29:48 +02:00
parent 4f9dd1016d
commit 9f6ce647ce
2 changed files with 209 additions and 4 deletions

View file

@ -0,0 +1,199 @@
import { mock } from 'jest-mock-extended';
import { FORM_NODE_TYPE, type Workflow } from 'n8n-workflow';
import type { ExecutionRepository } from '@/databases/repositories/execution.repository';
import { WaitingForms } from '@/webhooks/waiting-forms';
describe('WaitingForms', () => {
const executionRepository = mock<ExecutionRepository>();
const waitingWebhooks = new WaitingForms(mock(), mock(), executionRepository);
beforeEach(() => {
jest.restoreAllMocks();
});
describe('findCompletionPage', () => {
it('should return lastNodeExecuted if it is a non-disabled form completion node', () => {
const workflow = mock<Workflow>({
getParentNodes: jest.fn().mockReturnValue([]),
nodes: {
Form1: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
},
});
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'Form1');
expect(result).toBe('Form1');
});
it('should return undefined if lastNodeExecuted is disabled', () => {
const workflow = mock<Workflow>({
getParentNodes: jest.fn().mockReturnValue([]),
nodes: {
Form1: {
disabled: true,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
},
});
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'Form1');
expect(result).toBeUndefined();
});
it('should return undefined if lastNodeExecuted is not a form node', () => {
const workflow = mock<Workflow>({
getParentNodes: jest.fn().mockReturnValue([]),
nodes: {
NonForm: {
disabled: undefined,
type: 'other-node-type',
parameters: {},
},
},
});
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'NonForm');
expect(result).toBeUndefined();
});
it('should return undefined if lastNodeExecuted operation is not completion', () => {
const workflow = mock<Workflow>({
getParentNodes: jest.fn().mockReturnValue([]),
nodes: {
Form1: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'page',
},
},
},
});
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'Form1');
expect(result).toBeUndefined();
});
it('should find first valid completion form in parent nodes if lastNodeExecuted is not valid', () => {
const workflow = mock<Workflow>({
getParentNodes: jest.fn().mockReturnValue(['Form1', 'Form2', 'Form3']),
nodes: {
LastNode: {
disabled: undefined,
type: 'other-node-type',
parameters: {},
},
Form1: {
disabled: true,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
Form2: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
Form3: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
},
});
const runData = {
Form2: [],
Form3: [],
};
const result = waitingWebhooks.findCompletionPage(workflow, runData, 'LastNode');
expect(result).toBe('Form3');
});
it('should return undefined if no valid completion form is found in parent nodes', () => {
const workflow = mock<Workflow>({
getParentNodes: jest.fn().mockReturnValue(['Form1', 'Form2']),
nodes: {
LastNode: {
disabled: undefined,
type: 'other-node-type',
parameters: {},
},
Form1: {
disabled: true,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
Form2: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'submit',
},
},
},
});
const result = waitingWebhooks.findCompletionPage(workflow, {}, 'LastNode');
expect(result).toBeUndefined();
});
it('should skip parent nodes without runData', () => {
const workflow = mock<Workflow>({
getParentNodes: jest.fn().mockReturnValue(['Form1', 'Form2', 'Form3']),
nodes: {
LastNode: {
disabled: undefined,
type: 'other-node-type',
parameters: {},
},
Form1: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
Form2: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
Form3: {
disabled: undefined,
type: FORM_NODE_TYPE,
parameters: {
operation: 'completion',
},
},
},
});
const runData = {
Form2: [],
};
const result = waitingWebhooks.findCompletionPage(workflow, runData, 'LastNode');
expect(result).toBe('Form2');
});
});
});

View file

@ -1,5 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import type express from 'express'; import type express from 'express';
import type { IRunData } from 'n8n-workflow';
import { FORM_NODE_TYPE, sleep, Workflow } from 'n8n-workflow'; import { FORM_NODE_TYPE, sleep, Workflow } from 'n8n-workflow';
import { Service } from 'typedi'; import { Service } from 'typedi';
@ -57,8 +58,7 @@ export class WaitingForms extends WaitingWebhooks {
} catch (error) {} } catch (error) {}
} }
private findCompletionPage(execution: IExecutionResponse, lastNodeExecuted: string) { findCompletionPage(workflow: Workflow, runData: IRunData, lastNodeExecuted: string) {
const workflow = this.getWorkflow(execution);
const parentNodes = workflow.getParentNodes(lastNodeExecuted); const parentNodes = workflow.getParentNodes(lastNodeExecuted);
const lastNode = workflow.nodes[lastNodeExecuted]; const lastNode = workflow.nodes[lastNodeExecuted];
@ -75,7 +75,7 @@ export class WaitingForms extends WaitingWebhooks {
!node.disabled && !node.disabled &&
node.type === FORM_NODE_TYPE && node.type === FORM_NODE_TYPE &&
node.parameters.operation === 'completion' && node.parameters.operation === 'completion' &&
execution.data.resultData.runData[nodeName] runData[nodeName]
); );
}); });
} }
@ -118,7 +118,13 @@ export class WaitingForms extends WaitingWebhooks {
if (execution.finished) { if (execution.finished) {
// find the completion page to render // find the completion page to render
// if there is no completion page, render the default page // if there is no completion page, render the default page
const completionPage = this.findCompletionPage(execution, lastNodeExecuted); const workflow = this.getWorkflow(execution);
const completionPage = this.findCompletionPage(
workflow,
execution.data.resultData.runData,
lastNodeExecuted,
);
if (!completionPage) { if (!completionPage) {
res.render('form-trigger-completion', { res.render('form-trigger-completion', {