diff --git a/.vscode/launch.json b/.vscode/launch.json index 448d745236..5501fe4439 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -47,9 +47,7 @@ "request": "launch", "skipFiles": ["/**"], "type": "node", - "env": { - // "N8N_PORT": "5679", - }, + "envFile": "${workspaceFolder}/.env", "outputCapture": "std", "killBehavior": "polite" }, diff --git a/CHANGELOG.md b/CHANGELOG.md index f85ab264be..7c7a1e036f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,67 @@ +# [1.68.0](https://github.com/n8n-io/n8n/compare/n8n@1.67.0...n8n@1.68.0) (2024-11-13) + + +### Bug Fixes + +* **AI Agent Node:** Throw better errors for non-tool agents when using structured tools ([#11582](https://github.com/n8n-io/n8n/issues/11582)) ([9b6123d](https://github.com/n8n-io/n8n/commit/9b6123dfb2648f880c7829211fa07666611ad0ea)) +* **Auto-fixing Output Parser Node:** Only run retry chain on parsing errors ([#11569](https://github.com/n8n-io/n8n/issues/11569)) ([21b31e4](https://github.com/n8n-io/n8n/commit/21b31e488ff6ab0bcf3c79edcd17b9e37d4c64a4)) +* **core:** Continue with error output reverse items in success branch ([#11684](https://github.com/n8n-io/n8n/issues/11684)) ([6d5ee83](https://github.com/n8n-io/n8n/commit/6d5ee832966fab96043b0d65697c059ced61d334)) +* **core:** Ensure task runner server closes websocket connection correctly ([#11633](https://github.com/n8n-io/n8n/issues/11633)) ([b496bf3](https://github.com/n8n-io/n8n/commit/b496bf3147d2cd873d24371be02cb7ea5dbd8621)) +* **core:** Handle websocket connection error more gracefully in task runners ([#11635](https://github.com/n8n-io/n8n/issues/11635)) ([af7d6e6](https://github.com/n8n-io/n8n/commit/af7d6e68d0436ff8a3d4e8410dc8ee4f3a035c44)) +* **core:** Improve model sub-nodes error handling ([#11418](https://github.com/n8n-io/n8n/issues/11418)) ([57467d0](https://github.com/n8n-io/n8n/commit/57467d0285d67509322630c4c01130022f274a41)) +* **core:** Make push work for waiting webhooks ([#11678](https://github.com/n8n-io/n8n/issues/11678)) ([600479b](https://github.com/n8n-io/n8n/commit/600479bf36ba8870d4aecacad19a2dc5f2d97959)) +* **core:** Revert all the context helpers changes ([#11616](https://github.com/n8n-io/n8n/issues/11616)) ([20fd38f](https://github.com/n8n-io/n8n/commit/20fd38f3517f7ef35604ba16abb4d951270b4d50)) +* **core:** Set the authentication methad to `email` during startup if the SAML configuration in the database has been corrupted ([#11600](https://github.com/n8n-io/n8n/issues/11600)) ([6439291](https://github.com/n8n-io/n8n/commit/6439291738dec16261979d6d835acbc63743d51a)) +* **core:** Use cached value in retrieval of personal project owner ([#11533](https://github.com/n8n-io/n8n/issues/11533)) ([04029d8](https://github.com/n8n-io/n8n/commit/04029d82a11b52990890380ba7094055b18e7c1f)) +* Credentials save button is hidden unless you make changes to the ([#11492](https://github.com/n8n-io/n8n/issues/11492)) ([835fbfe](https://github.com/n8n-io/n8n/commit/835fbfe337dd8dc0d0b0318c7227e174484e1328)) +* **editor:** Add stickies to node insert position conflict check allowlist ([#11624](https://github.com/n8n-io/n8n/issues/11624)) ([fc39e3c](https://github.com/n8n-io/n8n/commit/fc39e3ca16231c176957e2504d55df6b416874fe)) +* **editor:** Adjust Scrollbar Width of RunData Header Row ([#11561](https://github.com/n8n-io/n8n/issues/11561)) ([d17d76a](https://github.com/n8n-io/n8n/commit/d17d76a85d5425bc091d29fc84605ffbccbca984)) +* **editor:** Cap NDV Output View Tab Index to prevent rare edge case ([#11614](https://github.com/n8n-io/n8n/issues/11614)) ([a6c8ee4](https://github.com/n8n-io/n8n/commit/a6c8ee4a82e6055766dc1307f79c774c17bb5f4d)) +* **editor:** Do not show hover tooltip when autocomplete is active ([#11653](https://github.com/n8n-io/n8n/issues/11653)) ([23caf43](https://github.com/n8n-io/n8n/commit/23caf43e30342a21d45c825f438aa1e6193601d1)) +* **editor:** Enable pinning main output with error and always allow unpinning ([#11452](https://github.com/n8n-io/n8n/issues/11452)) ([40c8882](https://github.com/n8n-io/n8n/commit/40c88822acdcda6401bd92b9cf89d013c44b8453)) +* **editor:** Fix collapsing nested items in expression modal schema view ([#11645](https://github.com/n8n-io/n8n/issues/11645)) ([41dea52](https://github.com/n8n-io/n8n/commit/41dea522fbfb1c9acee51f47f384973914454b5f)) +* **editor:** Fix default workflow settings ([#11632](https://github.com/n8n-io/n8n/issues/11632)) ([658568e](https://github.com/n8n-io/n8n/commit/658568e2700bfd5b61da53f3052403d0098c2d90)) +* **editor:** Fix duplicate chat trigger ([#11693](https://github.com/n8n-io/n8n/issues/11693)) ([a025848](https://github.com/n8n-io/n8n/commit/a025848ec4be96f74d4de2ab104256b6d89bb837)) +* **editor:** Fix hiding SQL query output when trying to select ([#11649](https://github.com/n8n-io/n8n/issues/11649)) ([4dbf2f4](https://github.com/n8n-io/n8n/commit/4dbf2f4256111985b367030020f1494b8a8b95af)) +* **editor:** Fix scrolling in code edit modal ([#11647](https://github.com/n8n-io/n8n/issues/11647)) ([8f695f3](https://github.com/n8n-io/n8n/commit/8f695f3417820e7b9bb04b78972f6abbd61abbe8)) +* **editor:** Prevent error being thrown in RLC while loading ([#11676](https://github.com/n8n-io/n8n/issues/11676)) ([ca8cb45](https://github.com/n8n-io/n8n/commit/ca8cb455ba59831295c238afb11aeab6ad18428e)) +* **editor:** Prevent NodeCreator from swallowing AskAssistant enter event ([#11532](https://github.com/n8n-io/n8n/issues/11532)) ([db94f16](https://github.com/n8n-io/n8n/commit/db94f169fcd03983fc78a3b4c5e11543610825bf)) +* **editor:** Show node executing status shortly before switching to success on new canvas ([#11675](https://github.com/n8n-io/n8n/issues/11675)) ([b0ba24c](https://github.com/n8n-io/n8n/commit/b0ba24cbbc55cebc26e9f62ead7396c4c8fbd062)) +* **editor:** Show only error title and 'Open errored node' button; hide 'Ask Assistant' in root for sub-node errors ([#11573](https://github.com/n8n-io/n8n/issues/11573)) ([8cba100](https://github.com/n8n-io/n8n/commit/8cba1004888f60ca653ee069501c13b3cadcc561)) +* **Facebook Lead Ads Trigger Node:** Fix issue with optional fields ([#11692](https://github.com/n8n-io/n8n/issues/11692)) ([70d315b](https://github.com/n8n-io/n8n/commit/70d315b3d5b8f5f3e0f39527bba11e254a52028e)) +* **Google BigQuery Node:** Add item index to insert error ([#11702](https://github.com/n8n-io/n8n/issues/11702)) ([145d092](https://github.com/n8n-io/n8n/commit/145d0921b217bbd4b625beaacfa14429560bf51b)) +* **Google Drive Node:** Fix file upload for streams ([#11698](https://github.com/n8n-io/n8n/issues/11698)) ([770230f](https://github.com/n8n-io/n8n/commit/770230fbfe0b9e86527254e201c4602fbced94ff)) +* **In-Memory Vector Store Node:** Fix displaying execution data of connected embedding nodes ([#11701](https://github.com/n8n-io/n8n/issues/11701)) ([40ade15](https://github.com/n8n-io/n8n/commit/40ade151724f4af28a6ed959fd9363450ea711fd)) +* **Item List Output Parser Node:** Fix number of items parameter issue ([#11696](https://github.com/n8n-io/n8n/issues/11696)) ([01ebe9d](https://github.com/n8n-io/n8n/commit/01ebe9dd38629afbab954fb489f3ef2bb7ab5b34)) +* **n8n Form Node:** Find completion page ([#11674](https://github.com/n8n-io/n8n/issues/11674)) ([ed3ad6d](https://github.com/n8n-io/n8n/commit/ed3ad6d684597f7c4b7419dfa81d476e66f10eba)) +* **n8n Form Node:** Open form page if form trigger has pin data ([#11673](https://github.com/n8n-io/n8n/issues/11673)) ([f0492bd](https://github.com/n8n-io/n8n/commit/f0492bd3bb0d94802a2707fb1cf861313b6ea808)) +* **n8n Form Node:** Trigger page stack in waiting if error in workflow ([#11671](https://github.com/n8n-io/n8n/issues/11671)) ([94b5873](https://github.com/n8n-io/n8n/commit/94b5873248212a5500f02cf3c0d74df6f9d8fb26)) +* **n8n Form Trigger Node:** Checkboxes different sizes ([#11677](https://github.com/n8n-io/n8n/issues/11677)) ([c08d23c](https://github.com/n8n-io/n8n/commit/c08d23c00f01bb6fcb3b75f02e0338af375f9b32)) +* NDV search bugs ([#11613](https://github.com/n8n-io/n8n/issues/11613)) ([c32cf64](https://github.com/n8n-io/n8n/commit/c32cf644a6b8c21558e802449329877845de70b1)) +* **Notion Node:** Extract page url ([#11643](https://github.com/n8n-io/n8n/issues/11643)) ([cbdd535](https://github.com/n8n-io/n8n/commit/cbdd535fe0cb4e032ea82f008dcf35cc5f2264c2)) +* **Redis Chat Memory Node:** Respect the SSL flag from the credential ([#11689](https://github.com/n8n-io/n8n/issues/11689)) ([b5cbf75](https://github.com/n8n-io/n8n/commit/b5cbf7566d351d8a8e9972f13ff5867ff1c8d7d0)) +* **Supabase Node:** Reset query parameters in get many operation ([#11630](https://github.com/n8n-io/n8n/issues/11630)) ([7458229](https://github.com/n8n-io/n8n/commit/74582290c04d2dd32300b1a6c7715862ae837d34)) +* **Switch Node:** Maintain output connections ([#11162](https://github.com/n8n-io/n8n/issues/11162)) ([9bd79fc](https://github.com/n8n-io/n8n/commit/9bd79fceebc4453d0fe40ae5f628d5e31ff2b326)) + + +### Features + +* **AI Transform Node:** Show warning for binary data ([#11560](https://github.com/n8n-io/n8n/issues/11560)) ([ddbb263](https://github.com/n8n-io/n8n/commit/ddbb263dce0fc458abc95d850217251bb49d2b83)) +* **core:** Make all http requests made with `httpRequestWithAuthentication` abortable ([#11704](https://github.com/n8n-io/n8n/issues/11704)) ([0d8aada](https://github.com/n8n-io/n8n/commit/0d8aada49005d6a6078e8460003a0de61a8f423c)) +* **editor:** Improve how we show default Agent prompt and Memory session parameters ([#11491](https://github.com/n8n-io/n8n/issues/11491)) ([565f8cd](https://github.com/n8n-io/n8n/commit/565f8cd8c78b534a50e272997d659d162fa86625)) +* **editor:** Improve workflow loading performance on new canvas ([#11629](https://github.com/n8n-io/n8n/issues/11629)) ([f1e2df7](https://github.com/n8n-io/n8n/commit/f1e2df7d0753aa0f33cf299100a063bf89cc8b35)) +* **editor:** Redesign Canvas Chat ([#11634](https://github.com/n8n-io/n8n/issues/11634)) ([a412ab7](https://github.com/n8n-io/n8n/commit/a412ab7ebfcd6aa9051a8ca36e34f1067102c998)) +* **editor:** Restrict when a ChatTrigger Node is added automatically ([#11523](https://github.com/n8n-io/n8n/issues/11523)) ([93a6f85](https://github.com/n8n-io/n8n/commit/93a6f858fa3eb53f8b48b2a3d6b7377279dd6ed1)) +* Github star button in-app ([#11695](https://github.com/n8n-io/n8n/issues/11695)) ([0fd684d](https://github.com/n8n-io/n8n/commit/0fd684d90c830f8b0aab12b7f78a1fa5619c62c9)) +* **Oura Node:** Update node for v2 api ([#11604](https://github.com/n8n-io/n8n/issues/11604)) ([3348fbb](https://github.com/n8n-io/n8n/commit/3348fbb1547c430ff8707b298640e3461d3f6536)) + + +### Performance Improvements + +* **editor:** Add lint rules for optimization-friendly syntax ([#11681](https://github.com/n8n-io/n8n/issues/11681)) ([88295c7](https://github.com/n8n-io/n8n/commit/88295c70495ae3d017674d5745972a346fcbaf12)) + + + # [1.67.0](https://github.com/n8n-io/n8n/compare/n8n@1.66.0...n8n@1.67.0) (2024-11-06) diff --git a/cypress/composables/modals/chat-modal.ts b/cypress/composables/modals/chat-modal.ts index 254d811a18..220c363dd1 100644 --- a/cypress/composables/modals/chat-modal.ts +++ b/cypress/composables/modals/chat-modal.ts @@ -3,7 +3,7 @@ */ export function getManualChatModal() { - return cy.getByTestId('lmChat-modal'); + return cy.getByTestId('canvas-chat'); } export function getManualChatInput() { @@ -19,11 +19,11 @@ export function getManualChatMessages() { } export function getManualChatModalCloseButton() { - return getManualChatModal().get('.el-dialog__close'); + return cy.getByTestId('workflow-chat-button'); } export function getManualChatModalLogs() { - return getManualChatModal().getByTestId('lm-chat-logs'); + return cy.getByTestId('canvas-chat-logs'); } export function getManualChatDialog() { return getManualChatModal().getByTestId('workflow-lm-chat-dialog'); diff --git a/cypress/composables/projects.ts b/cypress/composables/projects.ts index da9c6fcc65..52a28cba62 100644 --- a/cypress/composables/projects.ts +++ b/cypress/composables/projects.ts @@ -11,6 +11,7 @@ export const getAddProjectButton = () => export const getProjectTabs = () => cy.getByTestId('project-tabs').find('a'); export const getProjectTabWorkflows = () => getProjectTabs().filter('a[href$="/workflows"]'); export const getProjectTabCredentials = () => getProjectTabs().filter('a[href$="/credentials"]'); +export const getProjectTabExecutions = () => getProjectTabs().filter('a[href$="/executions"]'); export const getProjectTabSettings = () => getProjectTabs().filter('a[href$="/settings"]'); export const getProjectSettingsNameInput = () => cy.getByTestId('project-settings-name-input').find('input'); diff --git a/cypress/e2e/1-workflows.cy.ts b/cypress/e2e/1-workflows.cy.ts index a6683bbee4..eb93c07ee6 100644 --- a/cypress/e2e/1-workflows.cy.ts +++ b/cypress/e2e/1-workflows.cy.ts @@ -23,6 +23,7 @@ describe('Workflows', () => { }); it('should create multiple new workflows using add workflow button', () => { + cy.viewport(1920, 1080); [...Array(multipleWorkflowsCount).keys()].forEach(() => { cy.visit(WorkflowsPage.url); WorkflowsPage.getters.createWorkflowButton().click(); @@ -35,6 +36,7 @@ describe('Workflows', () => { }); it('should search for a workflow', () => { + cy.viewport(1920, 1080); // One Result WorkflowsPage.getters.searchBar().type('Empty State Card Workflow'); WorkflowsPage.getters.workflowCards().should('have.length', 1); @@ -60,6 +62,7 @@ describe('Workflows', () => { }); it('should delete all the workflows', () => { + cy.viewport(1920, 1080); WorkflowsPage.getters.workflowCards().should('have.length', multipleWorkflowsCount + 1); WorkflowsPage.getters.workflowCards().each(($el) => { @@ -75,6 +78,7 @@ describe('Workflows', () => { }); it('should respect tag querystring filter when listing workflows', () => { + cy.viewport(1920, 1080); WorkflowsPage.getters.newWorkflowButtonCard().click(); cy.createFixtureWorkflow('Test_workflow_2.json', getUniqueWorkflowName('My New Workflow')); diff --git a/cypress/e2e/17-workflow-tags.cy.ts b/cypress/e2e/17-workflow-tags.cy.ts index 26ea7cbe2c..e8dacca2b8 100644 --- a/cypress/e2e/17-workflow-tags.cy.ts +++ b/cypress/e2e/17-workflow-tags.cy.ts @@ -53,6 +53,7 @@ describe('Workflow tags', () => { }); it('should detach a tag inline by clicking on X on tag pill', () => { + cy.viewport(1920, 1080); wf.getters.createTagButton().click(); wf.actions.addTags(TEST_TAGS); wf.getters.nthTagPill(1).click(); @@ -73,6 +74,7 @@ describe('Workflow tags', () => { }); it('should not show non existing tag as a selectable option', () => { + cy.viewport(1920, 1080); const NON_EXISTING_TAG = 'My Test Tag'; wf.getters.createTagButton().click(); diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts index 5be2399253..b8b30dd7a9 100644 --- a/cypress/e2e/19-execution.cy.ts +++ b/cypress/e2e/19-execution.cy.ts @@ -514,6 +514,7 @@ describe('Execution', () => { }); it('should send proper payload for node rerun', () => { + cy.viewport(1920, 1080); cy.createFixtureWorkflow('Multiple_trigger_node_rerun.json', 'Multiple trigger node rerun'); workflowPage.getters.zoomToFitButton().click(); diff --git a/cypress/e2e/20-workflow-executions.cy.ts b/cypress/e2e/20-workflow-executions.cy.ts index 5788af171c..bfa9bca1d9 100644 --- a/cypress/e2e/20-workflow-executions.cy.ts +++ b/cypress/e2e/20-workflow-executions.cy.ts @@ -101,6 +101,7 @@ describe('Workflow Executions', () => { }); it('should show workflow data in executions tab after hard reload and modify name and tags', () => { + cy.viewport(1920, 1080); executionsTab.actions.switchToExecutionsTab(); checkMainHeaderELements(); workflowPage.getters.saveButton().find('button').should('not.exist'); diff --git a/cypress/e2e/30-langchain.cy.ts b/cypress/e2e/30-langchain.cy.ts index c4c3ff9e6e..23e8c9529b 100644 --- a/cypress/e2e/30-langchain.cy.ts +++ b/cypress/e2e/30-langchain.cy.ts @@ -14,7 +14,6 @@ import { } from './../constants'; import { closeManualChatModal, - getManualChatDialog, getManualChatMessages, getManualChatModal, getManualChatModalLogs, @@ -170,7 +169,7 @@ describe('Langchain Integration', () => { lastNodeExecuted: BASIC_LLM_CHAIN_NODE_NAME, }); - getManualChatDialog().should('contain', outputMessage); + getManualChatMessages().should('contain', outputMessage); }); it('should be able to open and execute Agent node', () => { @@ -210,7 +209,7 @@ describe('Langchain Integration', () => { lastNodeExecuted: AGENT_NODE_NAME, }); - getManualChatDialog().should('contain', outputMessage); + getManualChatMessages().should('contain', outputMessage); }); it('should add and use Manual Chat Trigger node together with Agent node', () => { @@ -231,8 +230,6 @@ describe('Langchain Integration', () => { clickManualChatButton(); - getManualChatModalLogs().should('not.exist'); - const inputMessage = 'Hello!'; const outputMessage = 'Hi there! How can I assist you today?'; const runData = [ @@ -337,6 +334,8 @@ describe('Langchain Integration', () => { getManualChatModalLogsEntries().should('have.length', 1); closeManualChatModal(); + getManualChatModalLogs().should('not.exist'); + getManualChatModal().should('not.exist'); }); it('should auto-add chat trigger and basic LLM chain when adding LLM node', () => { @@ -361,6 +360,14 @@ describe('Langchain Integration', () => { getConnectionBySourceAndTarget(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME).should('exist'); getNodes().should('have.length', 3); }); + it('should not auto-add nodes if ChatTrigger is already present', () => { + addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); + addNodeToCanvas(AGENT_NODE_NAME, true); + + addNodeToCanvas(AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, true); + getConnectionBySourceAndTarget(CHAT_TRIGGER_NODE_DISPLAY_NAME, AGENT_NODE_NAME).should('exist'); + getNodes().should('have.length', 3); + }); it('should render runItems for sub-nodes and allow switching between them', () => { const workflowPage = new WorkflowPage(); const ndv = new NDV(); diff --git a/cypress/e2e/34-template-credentials-setup.cy.ts b/cypress/e2e/34-template-credentials-setup.cy.ts index 815f4b1ceb..a2f9a019f7 100644 --- a/cypress/e2e/34-template-credentials-setup.cy.ts +++ b/cypress/e2e/34-template-credentials-setup.cy.ts @@ -182,6 +182,7 @@ describe('Template credentials setup', () => { }); it('should fill credentials from workflow editor', () => { + cy.viewport(1920, 1080); templateCredentialsSetupPage.visitTemplateCredentialSetupPage(testTemplate.id); templateCredentialsSetupPage.getters.skipLink().click(); diff --git a/cypress/e2e/39-projects.cy.ts b/cypress/e2e/39-projects.cy.ts index 84d062fff5..bbd0508662 100644 --- a/cypress/e2e/39-projects.cy.ts +++ b/cypress/e2e/39-projects.cy.ts @@ -51,7 +51,7 @@ describe('Projects', { disableAutoLogin: true }, () => { }); projects.getHomeButton().click(); - projects.getProjectTabs().should('have.length', 2); + projects.getProjectTabs().should('have.length', 3); projects.getProjectTabCredentials().click(); credentialsPage.getters.credentialCards().should('not.have.length'); @@ -101,7 +101,7 @@ describe('Projects', { disableAutoLogin: true }, () => { projects.getMenuItems().first().click(); workflowsPage.getters.workflowCards().should('not.have.length'); - projects.getProjectTabs().should('have.length', 3); + projects.getProjectTabs().should('have.length', 4); workflowsPage.getters.newWorkflowButtonCard().click(); @@ -176,7 +176,7 @@ describe('Projects', { disableAutoLogin: true }, () => { let menuItems = cy.getByTestId('menu-item'); menuItems.filter('[class*=active_]').should('have.length', 1); - menuItems.filter(':contains("Home")[class*=active_]').should('exist'); + menuItems.filter(':contains("Overview")[class*=active_]').should('exist'); projects.getMenuItems().first().click(); @@ -222,7 +222,7 @@ describe('Projects', { disableAutoLogin: true }, () => { menuItems = cy.getByTestId('menu-item'); menuItems.filter('[class*=active_]').should('have.length', 1); - menuItems.filter(':contains("Home")[class*=active_]').should('exist'); + menuItems.filter(':contains("Overview")[class*=active_]').should('exist'); workflowsPage.getters.workflowCards().should('have.length', 2).first().click(); @@ -230,7 +230,7 @@ describe('Projects', { disableAutoLogin: true }, () => { cy.getByTestId('execute-workflow-button').should('be.visible'); menuItems = cy.getByTestId('menu-item'); - menuItems.filter(':contains("Home")[class*=active_]').should('not.exist'); + menuItems.filter(':contains("Overview")[class*=active_]').should('not.exist'); menuItems = cy.getByTestId('menu-item'); menuItems.filter('[class*=active_]').should('have.length', 1); @@ -441,9 +441,7 @@ describe('Projects', { disableAutoLogin: true }, () => { .should('contain.text', 'Notion account personal project'); }); - // Skip flaky test - // eslint-disable-next-line n8n-local-rules/no-skipped-tests - it.skip('should move resources between projects', () => { + it('should move resources between projects', () => { cy.signinAsOwner(); cy.visit(workflowsPage.url); @@ -686,9 +684,7 @@ describe('Projects', { disableAutoLogin: true }, () => { .should('have.length', 1); }); - // Skip flaky test - // eslint-disable-next-line n8n-local-rules/no-skipped-tests - it.skip('should allow to change inaccessible credential when the workflow was moved to a team project', () => { + it('should allow to change inaccessible credential when the workflow was moved to a team project', () => { cy.signinAsOwner(); cy.visit(workflowsPage.url); @@ -701,9 +697,7 @@ describe('Projects', { disableAutoLogin: true }, () => { projects.getHomeButton().click(); workflowsPage.getters.workflowCards().should('not.have.length'); workflowsPage.getters.newWorkflowButtonCard().click(); - workflowsPage.getters.workflowCards().should('not.have.length'); - workflowsPage.getters.newWorkflowButtonCard().click(); workflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME); workflowPage.actions.addNodeToCanvas(NOTION_NODE_NAME, true, true); ndv.getters.backToCanvas().click(); @@ -789,7 +783,8 @@ describe('Projects', { disableAutoLogin: true }, () => { cy.get('input[name="password"]').type(INSTANCE_MEMBERS[0].password); cy.getByTestId('form-submit-button').click(); - mainSidebar.getters.executions().click(); + projects.getMenuItems().last().click(); + projects.getProjectTabExecutions().click(); cy.getByTestId('global-execution-list-item').first().find('td:last button').click(); getVisibleDropdown() .find('li') diff --git a/cypress/e2e/45-ai-assistant.cy.ts b/cypress/e2e/45-ai-assistant.cy.ts index 9c69269b33..7f9c79e0e6 100644 --- a/cypress/e2e/45-ai-assistant.cy.ts +++ b/cypress/e2e/45-ai-assistant.cy.ts @@ -36,6 +36,7 @@ describe('AI Assistant::enabled', () => { }); it('renders placeholder UI', () => { + cy.viewport(1920, 1080); aiAssistant.getters.askAssistantFloatingButton().should('be.visible'); aiAssistant.getters.askAssistantFloatingButton().click(); aiAssistant.getters.askAssistantChat().should('be.visible'); @@ -224,6 +225,54 @@ describe('AI Assistant::enabled', () => { .should('contain.text', 'item.json.myNewField = 1'); }); + it('Should ignore node execution success and error messages after the node run successfully once', () => { + const getParameter = () => ndv.getters.parameterInput('jsCode').should('be.visible'); + + const getEditor = () => getParameter().find('.cm-content').should('exist'); + + cy.intercept('POST', '/rest/ai/chat', { + statusCode: 200, + fixture: 'aiAssistant/responses/code_diff_suggestion_response.json', + }).as('chatRequest'); + + cy.createFixtureWorkflow('aiAssistant/workflows/test_workflow.json'); + wf.actions.openNode('Code'); + ndv.getters.nodeExecuteButton().click(); + aiAssistant.getters.nodeErrorViewAssistantButton().click({ force: true }); + cy.wait('@chatRequest'); + + cy.intercept('POST', '/rest/ai/chat', { + statusCode: 200, + fixture: 'aiAssistant/responses/node_execution_succeeded_response.json', + }).as('chatRequest2'); + + getEditor() + .type('{selectall}') + .paste( + 'for (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();', + ); + + ndv.getters.nodeExecuteButton().click(); + + getEditor() + .type('{selectall}') + .paste( + 'for (const item of $input.all()) {\n item.json.myNewField = 1aaaa!;\n}\n\nreturn $input.all();', + ); + + ndv.getters.nodeExecuteButton().click(); + + aiAssistant.getters.chatMessagesAssistant().should('have.length', 3); + + aiAssistant.getters + .chatMessagesAssistant() + .eq(2) + .should( + 'contain.text', + 'Code node ran successfully, did my solution help resolve your issue?\nQuick reply πŸ‘‡Yes, thanksNo, I am still stuck', + ); + }); + it('should end chat session when `end_session` event is received', () => { cy.intercept('POST', '/rest/ai/chat', { statusCode: 200, diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 70a62bb244..9ad1fceae1 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -66,6 +66,7 @@ describe('NDV', () => { }); it('should disconect Switch outputs if rules order was changed', () => { + cy.viewport(1920, 1080); cy.createFixtureWorkflow('NDV-test-switch_reorder.json', 'NDV test switch reorder'); workflowPage.actions.zoomToFit(); @@ -81,7 +82,7 @@ describe('NDV', () => { ndv.getters.backToCanvas().click(); workflowPage.actions.executeWorkflow(); workflowPage.actions.openNode('Merge'); - ndv.getters.outputPanel().contains('1 item').should('exist'); + ndv.getters.outputPanel().contains('2 items').should('exist'); cy.contains('span', 'zero').should('exist'); }); @@ -232,6 +233,7 @@ describe('NDV', () => { ndv.getters.outputPanel().find('[class*=_pagination]').should('exist'); }); it('should display large schema', () => { + cy.viewport(1920, 1080); cy.createFixtureWorkflow( 'Test_workflow_schema_test_pinned_data.json', 'NDV test schema view 2', @@ -718,6 +720,7 @@ describe('NDV', () => { }); it('Should open appropriate node creator after clicking on connection hint link', () => { + cy.viewport(1920, 1080); const nodeCreator = new NodeCreator(); const hintMapper = { Memory: 'AI Nodes', diff --git a/cypress/fixtures/aiAssistant/responses/node_execution_succeeded_response.json b/cypress/fixtures/aiAssistant/responses/node_execution_succeeded_response.json new file mode 100644 index 0000000000..62caf2a6b5 --- /dev/null +++ b/cypress/fixtures/aiAssistant/responses/node_execution_succeeded_response.json @@ -0,0 +1,22 @@ +{ + "sessionId": "1", + "messages": [ + { + "role": "assistant", + "type": "message", + "text": "**Code** node ran successfully, did my solution help resolve your issue?", + "quickReplies": [ + { + "text": "Yes, thanks", + "type": "all-good", + "isFeedback": true + }, + { + "text": "No, I am still stuck", + "type": "still-stuck", + "isFeedback": true + } + ] + } + ] +} diff --git a/package.json b/package.json index 1b35d0384f..bdf3221845 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-monorepo", - "version": "1.67.0", + "version": "1.68.0", "private": true, "engines": { "node": ">=20.15", @@ -16,6 +16,7 @@ "build:nodes": "turbo run build:nodes", "typecheck": "turbo typecheck", "dev": "turbo run dev --parallel --env-mode=loose --filter=!n8n-design-system --filter=!@n8n/chat --filter=!@n8n/task-runner", + "dev:be": "turbo run dev --parallel --env-mode=loose --filter=!n8n-design-system --filter=!@n8n/chat --filter=!@n8n/task-runner --filter=!n8n-editor-ui", "dev:ai": "turbo run dev --parallel --env-mode=loose --filter=@n8n/nodes-langchain --filter=n8n --filter=n8n-core", "clean": "turbo run clean --parallel", "reset": "node scripts/ensure-zx.mjs && zx scripts/reset.mjs", diff --git a/packages/@n8n/chat/package.json b/packages/@n8n/chat/package.json index a6098892f0..6ad77655b4 100644 --- a/packages/@n8n/chat/package.json +++ b/packages/@n8n/chat/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/chat", - "version": "0.30.0", + "version": "0.31.0", "scripts": { "dev": "pnpm run storybook", "build": "pnpm build:vite && pnpm build:bundle", diff --git a/packages/@n8n/chat/src/__tests__/setup.ts b/packages/@n8n/chat/src/__tests__/setup.ts index 7b0828bfa8..33e89fb68b 100644 --- a/packages/@n8n/chat/src/__tests__/setup.ts +++ b/packages/@n8n/chat/src/__tests__/setup.ts @@ -1 +1,13 @@ import '@testing-library/jest-dom'; +import '@testing-library/jest-dom'; +import { configure } from '@testing-library/vue'; + +configure({ testIdAttribute: 'data-test-id' }); + +window.ResizeObserver = + window.ResizeObserver || + vi.fn().mockImplementation(() => ({ + disconnect: vi.fn(), + observe: vi.fn(), + unobserve: vi.fn(), + })); diff --git a/packages/@n8n/chat/src/components/ChatFile.vue b/packages/@n8n/chat/src/components/ChatFile.vue index d3c3c2db7d..52b4acf789 100644 --- a/packages/@n8n/chat/src/components/ChatFile.vue +++ b/packages/@n8n/chat/src/components/ChatFile.vue @@ -30,22 +30,23 @@ const TypeIcon = computed(() => { }); function onClick() { - if (props.isRemovable) { - emit('remove', props.file); - } - if (props.isPreviewable) { window.open(URL.createObjectURL(props.file)); } } +function onDelete() { + emit('remove', props.file); +} @@ -80,12 +81,25 @@ function onClick() { .chat-file-preview { background: none; border: none; - display: none; + display: block; cursor: pointer; flex-shrink: 0; +} - .chat-file:hover & { - display: block; +.chat-file-delete { + position: relative; + &:hover { + color: red; + } + + /* Increase hit area for better clickability */ + &:before { + content: ''; + position: absolute; + top: -10px; + right: -10px; + bottom: -10px; + left: -10px; } } diff --git a/packages/@n8n/chat/src/components/Input.vue b/packages/@n8n/chat/src/components/Input.vue index 3e823917e0..4abfc76849 100644 --- a/packages/@n8n/chat/src/components/Input.vue +++ b/packages/@n8n/chat/src/components/Input.vue @@ -1,6 +1,6 @@