Skip to content

Commit f43cd7e

Browse files
committed
Merge branch 'master' into ado-3161-fe-combine-change-owner-and-move-to-folder-modals
2 parents f52b15c + 57d7b5e commit f43cd7e

File tree

174 files changed

+616
-458
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+616
-458
lines changed

codecov.yml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@ coverage:
77
patch: false
88
project:
99
default:
10-
threshold: 0.5%
11-
nodes:
12-
target: 80%
13-
threshold: 0.5%
14-
paths:
15-
- packages/nodes-base/**
10+
threshold: 0.5
1611

1712
github_checks:
1813
annotations: false
@@ -61,8 +56,14 @@ component_management:
6156
- packages/nodes-base/**
6257
- packages/@n8n/json-schema-to-zod/**
6358
- packages/@n8n/nodes-langchain/**
59+
statuses:
60+
- type: project
61+
target: auto
62+
threshold: 0% # Enforce: Coverage must not decrease
63+
64+
6465

6566
ignore:
6667
- (?s:.*/[^\/]*\.spec\.ts.*)\Z
6768
- (?s:.*/[^\/]*\.test\.ts.*)\Z
68-
- (?s:.*/[^\/]*e2e[^\/]*\.ts.*)\Z
69+
- (?s:.*/[^\/]*e2e[^\/]*\.ts.*)\Z

cypress/composables/ndv.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export function getInputSelect() {
3636
return cy.getByTestId('ndv-input-select').find('input');
3737
}
3838

39+
export function getInputLinkRun() {
40+
return getInputPanel().findChildByTestId('link-run');
41+
}
42+
3943
export function getMainPanel() {
4044
return cy.getByTestId('node-parameters');
4145
}
@@ -68,6 +72,14 @@ export function getInputTbodyCell(row: number, col: number) {
6872
return getInputTableRows().eq(row).find('td').eq(col);
6973
}
7074

75+
export function getInputRunSelector() {
76+
return getInputPanel().findChildByTestId('run-selector');
77+
}
78+
79+
export function getInputPanelItemsCount() {
80+
return getInputPanel().getByTestId('ndv-items-count');
81+
}
82+
7183
export function getOutputPanelDataContainer() {
7284
return getOutputPanel().findChildByTestId('ndv-data-container');
7385
}
@@ -329,3 +341,7 @@ export function resetHoverState() {
329341
export function setInputDisplayMode(mode: 'Schema' | 'Table' | 'JSON' | 'Binary') {
330342
getInputPanel().findChildByTestId('ndv-run-data-display-mode').contains(mode).click();
331343
}
344+
345+
export function toggleInputRunLinking() {
346+
getInputLinkRun().click();
347+
}

cypress/composables/workflow.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,35 +179,52 @@ export function executeWorkflowAndWait(waitForSuccessBannerToDisappear = true) {
179179
}
180180
}
181181

182+
/**
183+
* @param nodeDisplayName - The name of the node to add to the canvas
184+
* @param plusButtonClick - Whether to click the plus button to open the node creator
185+
* @param preventNdvClose - Whether to prevent the Ndv from closing
186+
* @param action - The action to select in the node creator
187+
* @param useExactMatch - Whether to use an exact match for the node name will use selector instead of enter key
188+
*/
182189
export function addNodeToCanvas(
183190
nodeDisplayName: string,
184191
plusButtonClick = true,
185192
preventNdvClose?: boolean,
186193
action?: string,
194+
useExactMatch = false,
187195
) {
188196
if (plusButtonClick) {
189197
getNodeCreatorPlusButton().click();
190198
}
191199

192200
getNodeCreatorSearchBar().type(nodeDisplayName);
193-
getNodeCreatorSearchBar().type('{enter}');
201+
202+
if (useExactMatch) {
203+
cy.getByTestId('node-creator-item-name').contains(nodeDisplayName).click();
204+
} else {
205+
getNodeCreatorSearchBar().type('{enter}');
206+
}
207+
194208
cy.wait(500);
209+
195210
cy.get('body').then((body) => {
196211
if (body.find('[data-test-id=node-creator]').length > 0) {
197212
if (action) {
198213
cy.contains(action).click();
199214
} else {
200-
// Select the first action
201215
cy.get('[data-keyboard-nav-type="action"]').eq(0).click();
202216
}
203217
}
204218
});
205219

206-
if (!preventNdvClose) cy.get('body').type('{esc}');
220+
if (!preventNdvClose) {
221+
cy.get('body').type('{esc}');
222+
}
207223
}
208224

209225
export function navigateToNewWorkflowPage(preventNodeViewUnload = true) {
210226
cy.visit(ROUTES.NEW_WORKFLOW_PAGE);
227+
cy.getByTestId('node-creator-plus-button').should('be.visible');
211228
cy.waitForLoad();
212229
cy.window().then((win) => {
213230
win.preventNodeViewBeforeUnload = preventNodeViewUnload;

cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ function createRunDataWithError(inputMessage: string) {
139139
function setupTestWorkflow(chatTrigger: boolean = false) {
140140
// Setup test workflow with AI Agent, Postgres Memory Node (source of error), Calculator Tool, and OpenAI Chat Model
141141
if (chatTrigger) {
142-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
142+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
143143
} else {
144144
addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true);
145145
}

cypress/e2e/30-langchain.cy.ts

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
sendManualChatMessage,
2222
} from '../composables/modals/chat-modal';
2323
import { setCredentialValues } from '../composables/modals/credential-modal';
24+
import * as ndv from '../composables/ndv';
2425
import {
2526
clickCreateNewCredential,
2627
clickExecuteNode,
@@ -29,6 +30,7 @@ import {
2930
getOutputPanelTable,
3031
checkParameterCheckboxInputByName,
3132
} from '../composables/ndv';
33+
import * as workflow from '../composables/workflow';
3234
import {
3335
addLanguageModelNodeToParent,
3436
addMemoryNodeToParent,
@@ -44,7 +46,7 @@ import {
4446
disableNode,
4547
getExecuteWorkflowButton,
4648
} from '../composables/workflow';
47-
import { NDV, WorkflowPage } from '../pages';
49+
import { WorkflowPage } from '../pages';
4850
import { createMockNodeExecutionData, runMockWorkflowExecution } from '../utils';
4951

5052
describe('Langchain Integration', () => {
@@ -132,7 +134,7 @@ describe('Langchain Integration', () => {
132134
});
133135

134136
it('should be able to open and execute Basic LLM Chain node', () => {
135-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
137+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
136138
addNodeToCanvas(BASIC_LLM_CHAIN_NODE_NAME, true);
137139

138140
addLanguageModelNodeToParent(
@@ -171,7 +173,7 @@ describe('Langchain Integration', () => {
171173
});
172174

173175
it('should be able to open and execute Agent node', () => {
174-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
176+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
175177
addNodeToCanvas(AGENT_NODE_NAME, true);
176178

177179
addLanguageModelNodeToParent(
@@ -211,7 +213,7 @@ describe('Langchain Integration', () => {
211213
});
212214

213215
it('should add and use Manual Chat Trigger node together with Agent node', () => {
214-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
216+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
215217
addNodeToCanvas(AGENT_NODE_NAME, true);
216218

217219
addLanguageModelNodeToParent(
@@ -362,7 +364,7 @@ describe('Langchain Integration', () => {
362364
getNodes().should('have.length', 3);
363365
});
364366
it('should not auto-add nodes if ChatTrigger is already present', () => {
365-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
367+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
366368
addNodeToCanvas(AGENT_NODE_NAME, true);
367369

368370
addNodeToCanvas(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, true);
@@ -372,59 +374,63 @@ describe('Langchain Integration', () => {
372374

373375
it('should render runItems for sub-nodes and allow switching between them', () => {
374376
const workflowPage = new WorkflowPage();
375-
const ndv = new NDV();
376377

377378
cy.visit(workflowPage.url);
378379
cy.createFixtureWorkflow('In_memory_vector_store_fake_embeddings.json');
379380
workflowPage.actions.zoomToFit();
380381
workflowPage.actions.deselectAll();
381382

382383
workflowPage.actions.executeNode('Populate VS');
384+
workflow.getNodesWithSpinner().should('not.exist');
383385

384386
const assertInputOutputText = (text: string, assertion: 'exist' | 'not.exist') => {
385-
ndv.getters.outputPanel().contains(text).should(assertion);
386-
ndv.getters.inputPanel().contains(text).should(assertion);
387+
ndv.getOutputPanel().contains(text).should(assertion);
388+
ndv.getOutputPanel().contains(text).should(assertion);
387389
};
388390

389391
workflowPage.actions.openNode('Character Text Splitter');
390-
ndv.getters.outputRunSelector().should('exist');
391-
ndv.getters.inputRunSelector().should('exist');
392-
ndv.getters.inputRunSelector().find('input').should('include.value', '3 of 3');
393-
ndv.getters.outputRunSelector().find('input').should('include.value', '3 of 3');
392+
393+
// Wait for the input panel to switch to Debugging mode
394+
ndv.getInputPanelItemsCount().should('not.exist');
395+
396+
ndv.getOutputRunSelector().should('exist');
397+
ndv.getInputRunSelector().should('exist');
398+
ndv.getInputRunSelector().find('input').should('include.value', '3 of 3');
399+
ndv.getOutputRunSelector().find('input').should('include.value', '3 of 3');
394400
assertInputOutputText('Kyiv', 'exist');
395401
assertInputOutputText('Berlin', 'not.exist');
396402
assertInputOutputText('Prague', 'not.exist');
397403

398-
ndv.actions.changeOutputRunSelector('2 of 3');
404+
ndv.changeOutputRunSelector('2 of 3');
399405
assertInputOutputText('Berlin', 'exist');
400406
assertInputOutputText('Kyiv', 'not.exist');
401407
assertInputOutputText('Prague', 'not.exist');
402408

403-
ndv.actions.changeOutputRunSelector('1 of 3');
409+
ndv.changeOutputRunSelector('1 of 3');
404410
assertInputOutputText('Prague', 'exist');
405411
assertInputOutputText('Berlin', 'not.exist');
406412
assertInputOutputText('Kyiv', 'not.exist');
407413

408-
ndv.actions.toggleInputRunLinking();
409-
ndv.actions.changeOutputRunSelector('2 of 3');
410-
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 3');
411-
ndv.getters.outputRunSelector().find('input').should('include.value', '2 of 3');
412-
ndv.getters.inputPanel().contains('Prague').should('exist');
413-
ndv.getters.inputPanel().contains('Berlin').should('not.exist');
414+
ndv.toggleInputRunLinking();
415+
ndv.changeOutputRunSelector('2 of 3');
416+
ndv.getInputRunSelector().find('input').should('include.value', '1 of 3');
417+
ndv.getOutputRunSelector().find('input').should('include.value', '2 of 3');
418+
ndv.getInputPanel().contains('Prague').should('exist');
419+
ndv.getInputPanel().contains('Berlin').should('not.exist');
414420

415-
ndv.getters.outputPanel().contains('Berlin').should('exist');
416-
ndv.getters.outputPanel().contains('Prague').should('not.exist');
421+
ndv.getOutputPanel().contains('Berlin').should('exist');
422+
ndv.getOutputPanel().contains('Prague').should('not.exist');
417423

418-
ndv.actions.toggleInputRunLinking();
419-
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 3');
420-
ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 3');
424+
ndv.toggleInputRunLinking();
425+
ndv.getInputRunSelector().find('input').should('include.value', '1 of 3');
426+
ndv.getOutputRunSelector().find('input').should('include.value', '1 of 3');
421427
assertInputOutputText('Prague', 'exist');
422428
assertInputOutputText('Berlin', 'not.exist');
423429
assertInputOutputText('Kyiv', 'not.exist');
424430
});
425431

426432
it('should show tool info notice if no existing tools were used during execution', () => {
427-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
433+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
428434
addNodeToCanvas(AGENT_NODE_NAME, true);
429435

430436
addLanguageModelNodeToParent(
@@ -469,7 +475,7 @@ describe('Langchain Integration', () => {
469475
});
470476

471477
it('should not show tool info notice if tools were used during execution', () => {
472-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
478+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
473479
addNodeToCanvas(AGENT_NODE_NAME, true, true);
474480
getRunDataInfoCallout().should('not.exist');
475481
clickGetBackToCanvas();
@@ -523,15 +529,14 @@ describe('Langchain Integration', () => {
523529

524530
it('should execute up to Node 1 when using partial execution', () => {
525531
const workflowPage = new WorkflowPage();
526-
const ndv = new NDV();
527532

528533
cy.visit(workflowPage.url);
529534
cy.createFixtureWorkflow('Test_workflow_chat_partial_execution.json');
530535
workflowPage.actions.zoomToFit();
531536

532537
getManualChatModal().find('main').should('not.exist');
533538
openNode('Node 1');
534-
ndv.actions.execute();
539+
ndv.clickExecuteNode();
535540

536541
getManualChatModal().find('main').should('exist');
537542
sendManualChatMessage('Test');

cypress/e2e/4-node-creator.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ describe('Node Creator', () => {
548548
});
549549

550550
it('should add node directly for sub-connection as tool', () => {
551-
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true);
551+
addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true, false, undefined, true);
552552
addNodeToCanvas(AGENT_NODE_NAME, true, true);
553553
clickGetBackToCanvas();
554554

cypress/e2e/48-subworkflow-inputs.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('Sub-workflow creation and typed usage', () => {
7070
// NAVIGATE TO CHILD WORKFLOW
7171
// **************************
7272
// Close NDV before opening the node creator
73-
cy.get('body').type('{esc}');
73+
clickGetBackToCanvas();
7474
openNode('When Executed by Another Workflow');
7575
});
7676

packages/@n8n/api-types/.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ const sharedOptions = require('@n8n/eslint-config/shared');
44
module.exports = {
55
extends: ['@n8n/eslint-config/base'],
66
...sharedOptions(__dirname),
7+
rules: {
8+
'unicorn/filename-case': ['error', { case: 'kebabCase' }],
9+
},
710
};

packages/@n8n/api-types/src/dto/credentials/credentials-get-many-request.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Z } from 'zod-class';
22

3-
import { booleanFromString } from '../../schemas/booleanFromString';
3+
import { booleanFromString } from '../../schemas/boolean-from-string';
44

55
export class CredentialsGetManyRequestQuery extends Z.class({
66
/**

packages/@n8n/api-types/src/dto/credentials/credentials-get-one-request.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Z } from 'zod-class';
22

3-
import { booleanFromString } from '../../schemas/booleanFromString';
3+
import { booleanFromString } from '../../schemas/boolean-from-string';
44

55
export class CredentialsGetOneRequestQuery extends Z.class({
66
/**

packages/@n8n/api-types/src/dto/dynamic-node-parameters/base-dynamic-parameters-request.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { INodeCredentials, INodeParameters, INodeTypeNameVersion } from 'n8
22
import { z } from 'zod';
33
import { Z } from 'zod-class';
44

5-
import { nodeVersionSchema } from '../../schemas/nodeVersion.schema';
5+
import { nodeVersionSchema } from '../../schemas/node-version.schema';
66

77
export class BaseDynamicParametersRequestDto extends Z.class({
88
path: z.string(),

packages/@n8n/api-types/src/dto/owner/__tests__/dismiss-banner-request.dto.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { bannerNameSchema } from '../../../schemas/bannerName.schema';
1+
import { bannerNameSchema } from '../../../schemas/banner-name.schema';
22
import { DismissBannerRequestDto } from '../dismiss-banner-request.dto';
33

44
describe('DismissBannerRequestDto', () => {

packages/@n8n/api-types/src/dto/owner/dismiss-banner-request.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Z } from 'zod-class';
22

3-
import { bannerNameSchema } from '../../schemas/bannerName.schema';
3+
import { bannerNameSchema } from '../../schemas/banner-name.schema';
44

55
export class DismissBannerRequestDto extends Z.class({
66
banner: bannerNameSchema.optional(),

packages/@n8n/api-types/src/dto/password-reset/change-password-request.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { z } from 'zod';
22
import { Z } from 'zod-class';
33

4+
import { passwordResetTokenSchema } from '../../schemas/password-reset-token.schema';
45
import { passwordSchema } from '../../schemas/password.schema';
5-
import { passwordResetTokenSchema } from '../../schemas/passwordResetToken.schema';
66

77
export class ChangePasswordRequestDto extends Z.class({
88
token: passwordResetTokenSchema,

packages/@n8n/api-types/src/dto/password-reset/resolve-password-token-query.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Z } from 'zod-class';
22

3-
import { passwordResetTokenSchema } from '../../schemas/passwordResetToken.schema';
3+
import { passwordResetTokenSchema } from '../../schemas/password-reset-token.schema';
44

55
export class ResolvePasswordTokenQueryDto extends Z.class({
66
token: passwordResetTokenSchema,

packages/@n8n/api-types/src/dto/tag/retrieve-tag-query.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Z } from 'zod-class';
22

3-
import { booleanFromString } from '../../schemas/booleanFromString';
3+
import { booleanFromString } from '../../schemas/boolean-from-string';
44

55
export class RetrieveTagQueryDto extends Z.class({
66
withUsageCount: booleanFromString.optional().default('false'),

0 commit comments

Comments
 (0)