From 111d2ebe689e75ba0ac69ed65673a76a09f50af7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:28:47 +0100 Subject: [PATCH 1/6] :rocket: Release 1.26.0 (#8427) Co-authored-by: ivov --- CHANGELOG.md | 47 ++++++++++++++++++++++ package.json | 2 +- packages/@n8n/chat/package.json | 2 +- packages/@n8n/client-oauth2/package.json | 2 +- packages/@n8n/nodes-langchain/package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/design-system/package.json | 2 +- packages/editor-ui/package.json | 2 +- packages/node-dev/package.json | 2 +- packages/nodes-base/package.json | 2 +- packages/workflow/package.json | 2 +- 12 files changed, 58 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index beb70c8ae4576..08cd3c2320ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +# [1.26.0](https://github.com/n8n-io/n8n/compare/n8n@1.25.0...n8n@1.26.0) (2024-01-24) + + +### Bug Fixes + +* **AMQP Trigger Node:** Properly close connection after manual test step ([#8396](https://github.com/n8n-io/n8n/issues/8396)) ([2c14371](https://github.com/n8n-io/n8n/commit/2c143714817e62fcb2fec9b9cac208ef029f87fd)) +* **Asana Node:** Fix issue when connecting to the new Asana environment ([#8404](https://github.com/n8n-io/n8n/issues/8404)) ([44f6ef2](https://github.com/n8n-io/n8n/commit/44f6ef2ed729ad39518cc85e068be03cd8b409ff)) +* **AWS SQS Node:** Fix issue preventing data from being sent correctly ([#8382](https://github.com/n8n-io/n8n/issues/8382)) ([daba5bb](https://github.com/n8n-io/n8n/commit/daba5bb250c3bee338dde96e6e815835dd21e6f1)) +* Change the UI text for some filter operations ([#8360](https://github.com/n8n-io/n8n/issues/8360)) ([976fe2e](https://github.com/n8n-io/n8n/commit/976fe2e6c8caf3f72c833af4447c46ddfa1d8e0a)) +* **core:** Adjust starter node priority for manual executions with pinned activators ([#8386](https://github.com/n8n-io/n8n/issues/8386)) ([749ac2b](https://github.com/n8n-io/n8n/commit/749ac2b407d9477343c169ac26daba1a36cfcc6d)) +* **core:** Errors are returned on the success branch if error item has other keys in addition to 'error' ([#8380](https://github.com/n8n-io/n8n/issues/8380)) ([25f51f4](https://github.com/n8n-io/n8n/commit/25f51f4fd79d14ccff8d35d92c11e47fe18f3e0d)) +* **core:** Fix removal of triggers and pollers from memory on deactivation in multi-main setup ([#8416](https://github.com/n8n-io/n8n/issues/8416)) ([2257ec6](https://github.com/n8n-io/n8n/commit/2257ec63b3716598f85a5237bc147bb2c887fa33)) +* **core:** Fix update workflow cli command being unable to activate all workflows ([#8412](https://github.com/n8n-io/n8n/issues/8412)) ([ae06fde](https://github.com/n8n-io/n8n/commit/ae06fdeb62d0cb982253f32956fdd55bd66058bd)) +* **core:** Missing pairedItem fixes ([#8394](https://github.com/n8n-io/n8n/issues/8394)) ([284d965](https://github.com/n8n-io/n8n/commit/284d965b5acc0819ffc109729ce1d5d0b2352abb)) +* **Discord Node:** Remove requirement on message for webhooks ([#8377](https://github.com/n8n-io/n8n/issues/8377)) ([c64e893](https://github.com/n8n-io/n8n/commit/c64e893b60143df6e9a752191adef0419811fe43)) +* **editor:** Add pinned data for freshly added nodes ([#8323](https://github.com/n8n-io/n8n/issues/8323)) ([83228e2](https://github.com/n8n-io/n8n/commit/83228e26fb8f62676e15d59f65a43106487034da)) +* **editor:** Enable ctrl/cmd click in workflow editor header ([#8387](https://github.com/n8n-io/n8n/issues/8387)) ([e43cf2f](https://github.com/n8n-io/n8n/commit/e43cf2fd715e21fd4e454c9e6b6d874306472360)) +* **editor:** Fix copy to clipboard on insecure contexts ([#8425](https://github.com/n8n-io/n8n/issues/8425)) ([7386f79](https://github.com/n8n-io/n8n/commit/7386f79362673876509c27a2f6ddef08125a0b1e)) +* **editor:** Fix doclines for `plus` and `minus` ([#8405](https://github.com/n8n-io/n8n/issues/8405)) ([ebf2b0d](https://github.com/n8n-io/n8n/commit/ebf2b0d55ccf3977269d4ea442f2ad210d1b375b)) +* **editor:** Fix invisible community package update button ([#8406](https://github.com/n8n-io/n8n/issues/8406)) ([2ccb754](https://github.com/n8n-io/n8n/commit/2ccb754e52949e0d20925871f425eef92cd6aebc)) +* **editor:** Fix secondary icon for environments on sidebar menu item ([#8407](https://github.com/n8n-io/n8n/issues/8407)) ([3544966](https://github.com/n8n-io/n8n/commit/35449667bfab7324350fe92e1f6538e3ae3cadb1)) +* **editor:** Open native context menu when editing Sticky ([#8370](https://github.com/n8n-io/n8n/issues/8370)) ([ade7d30](https://github.com/n8n-io/n8n/commit/ade7d30053f897bd9269912565d86d33046e560e)) +* **editor:** Use web native element in nav menus ([#8385](https://github.com/n8n-io/n8n/issues/8385)) ([e606e84](https://github.com/n8n-io/n8n/commit/e606e841ee1086d737849dc33f4ced867ab2cb21)) +* Fix issue preventing secrets with a - in the path from being imported ([#8378](https://github.com/n8n-io/n8n/issues/8378)) ([fc94377](https://github.com/n8n-io/n8n/commit/fc9437703687738091fdd072e03597358bd8f8d0)) +* Force posthog recording to be disabled outside cloud ([#8374](https://github.com/n8n-io/n8n/issues/8374)) ([f31cc07](https://github.com/n8n-io/n8n/commit/f31cc0743ff94c9b29cdd0d498c87340beb29585)) +* **Google Drive Node:** Fix issue preventing upload / update working in some configurations ([#8417](https://github.com/n8n-io/n8n/issues/8417)) ([4b3ea81](https://github.com/n8n-io/n8n/commit/4b3ea81028ae6dcbe3a4738dca7522b62685bb42)) +* **Microsoft Outlook Node:** Message -> Send with attachments ([#8238](https://github.com/n8n-io/n8n/issues/8238)) ([0128081](https://github.com/n8n-io/n8n/commit/01280815c950413188905f5d17a13157685d0a27)) +* **Microsoft SQL Node:** Prevent MSSQL max parameters error by chunking ([#8390](https://github.com/n8n-io/n8n/issues/8390)) ([1b0ba2c](https://github.com/n8n-io/n8n/commit/1b0ba2c02885a0dd9f548133efadcfc7485d666d)) +* **Notion Node:** Fix is_empty query on formula fields ([#8397](https://github.com/n8n-io/n8n/issues/8397)) ([08e7db4](https://github.com/n8n-io/n8n/commit/08e7db4648cd2f820e0d63d012f42057a124dd82)) +* **Switch Node:** Fix issue preventing some regex patterns from working ([#8422](https://github.com/n8n-io/n8n/issues/8422)) ([e9fea16](https://github.com/n8n-io/n8n/commit/e9fea16301cb9ee0ff7e3af45fc50d77e2cf6a23)) + + +### Features + +* **core:** Custom session timeout and refresh configuration ([#8342](https://github.com/n8n-io/n8n/issues/8342)) ([07e6705](https://github.com/n8n-io/n8n/commit/07e67052568dcb292dbf63bee8912110931726bc)) +* **core:** Email recipients on resource shared ([#8408](https://github.com/n8n-io/n8n/issues/8408)) ([a0a1830](https://github.com/n8n-io/n8n/commit/a0a1830696eaa905d37fbd56e8bc5035d12b2aa5)) +* **core:** Upgrade axios and follow-redirects to address CVE-2023-26159 ([#8366](https://github.com/n8n-io/n8n/issues/8366)) ([3912c5e](https://github.com/n8n-io/n8n/commit/3912c5e7abca98fe81d4c9c25894d30d8023ce56)) +* **core:** Upgrade bull and ioredis to address CVE-2023-52079 ([#8365](https://github.com/n8n-io/n8n/issues/8365)) ([639d347](https://github.com/n8n-io/n8n/commit/639d34769e75096d0725f1f60861dc972b344abe)) +* **editor:** Migrate `moveNodeWorkflow` mixin to `useCanvasPanning` composable ([#8322](https://github.com/n8n-io/n8n/issues/8322)) ([b6d7757](https://github.com/n8n-io/n8n/commit/b6d775768f927e69e7aa5f715c99f2fed4eaaa4c)) +* **Execute Workflow Node:** Add 'Wait For Sub-Workflow Completion' option ([#8389](https://github.com/n8n-io/n8n/issues/8389)) ([ff92fc7](https://github.com/n8n-io/n8n/commit/ff92fc7fef39076e0846f2426ffdd86c761e7896)) +* **LinkedIn Node:** Add support for Community Management API ([#7451](https://github.com/n8n-io/n8n/issues/7451)) ([7660d7e](https://github.com/n8n-io/n8n/commit/7660d7e735d248f3e731aca550c2973e85cdfebc)) +* **Microsoft Teams Node:** Overhaul ([#7477](https://github.com/n8n-io/n8n/issues/7477)) ([2c146cc](https://github.com/n8n-io/n8n/commit/2c146cca62ec605f6d722fe6c4b90c7df9cf77f7)) +* Nudge users to become template creators if eligible ([#8357](https://github.com/n8n-io/n8n/issues/8357)) ([9945701](https://github.com/n8n-io/n8n/commit/99457019f795636f56d80d3fc2c7e08055ace938)) +* **Telegram Trigger Node:** Verify Webhook requests ([#8383](https://github.com/n8n-io/n8n/issues/8383)) ([1117612](https://github.com/n8n-io/n8n/commit/11176124b5b6157c7c17fd882691d2f9d6b41487)) + + + # [1.25.0](https://github.com/n8n-io/n8n/compare/n8n@1.24.0...n8n@1.25.0) (2024-01-17) diff --git a/package.json b/package.json index 82755641b947b..c81b5808bfdb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.25.0", + "version": "1.26.0", "private": true, "homepage": "https://n8n.io", "engines": { diff --git a/packages/@n8n/chat/package.json b/packages/@n8n/chat/package.json index ec913f0fa47ca..a599117d35c79 100644 --- a/packages/@n8n/chat/package.json +++ b/packages/@n8n/chat/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/chat", - "version": "0.7.0", + "version": "0.8.0", "scripts": { "dev": "pnpm run storybook", "build": "pnpm type-check && pnpm build:vite && pnpm run build:individual && npm run build:prepare", diff --git a/packages/@n8n/client-oauth2/package.json b/packages/@n8n/client-oauth2/package.json index e3b3ee76575a2..b1613415fc10e 100644 --- a/packages/@n8n/client-oauth2/package.json +++ b/packages/@n8n/client-oauth2/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/client-oauth2", - "version": "0.12.0", + "version": "0.13.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 09ba700cf0742..b61f0735eb0d4 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-nodes-langchain", - "version": "0.10.0", + "version": "0.11.0", "description": "", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/cli/package.json b/packages/cli/package.json index 22e82a4e3b3fc..c1fe1774c6685 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.25.0", + "version": "1.26.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/core/package.json b/packages/core/package.json index 8d8052d2f1ced..2f253c8ca2b7f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "1.25.0", + "version": "1.26.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index f9de0a28f85ac..2d1a325af81f4 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "n8n-design-system", - "version": "1.19.0", + "version": "1.20.0", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", "author": { diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 966cde4a97187..27be63a5d75d1 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "1.25.0", + "version": "1.26.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 1db3517140191..c19cfc3a72fb0 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "1.25.0", + "version": "1.26.0", "description": "CLI to simplify n8n credentials/node development", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 34732e3cd4b90..207734c5e4d2d 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "1.25.0", + "version": "1.26.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 458274fb4deaf..fcfd922b726a4 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "1.25.0", + "version": "1.26.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", From 16a2ea5df0e46147d7b8a033278c20fe5b4e4b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 25 Jan 2024 13:06:45 +0100 Subject: [PATCH 2/6] ci: Move docs check notification to #alerts-build (no-changelog) (#8435) --- .github/workflows/check-documentation-urls.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-documentation-urls.yml b/.github/workflows/check-documentation-urls.yml index 99bd412105b3e..0d6de54fa70da 100644 --- a/.github/workflows/check-documentation-urls.yml +++ b/.github/workflows/check-documentation-urls.yml @@ -39,6 +39,7 @@ jobs: if: failure() with: status: ${{ job.status }} - channel: '#mission-docs' + channel: '#alerts-build' webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }} - message: Documentation URLs check failed (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + message: | + <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}| Documentation URLs check failed > From cc2f0ada76fe37bef17a3ab9744cd48d49a803b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 25 Jan 2024 14:33:35 +0100 Subject: [PATCH 3/6] ci: Enforce `noUnusedLocals` on all backend and nodes packages (no-changelog) (#8428) --- packages/@n8n/nodes-langchain/.eslintrc.js | 1 - packages/@n8n/nodes-langchain/tsconfig.json | 1 - packages/@n8n_io/eslint-config/base.js | 21 ++---------- .../cli/src/WorkflowExecuteAdditionalData.ts | 33 +++++-------------- packages/cli/src/WorkflowRunnerProcess.ts | 1 - packages/cli/src/commands/start.ts | 3 +- packages/cli/src/config/types.ts | 1 - .../MessageEventBusDestinationWebhook.ee.ts | 12 ------- packages/cli/tsconfig.json | 1 - packages/core/src/NodeExecuteFunctions.ts | 17 +++------- packages/node-dev/tsconfig.json | 1 - packages/nodes-base/.eslintrc.js | 1 - packages/workflow/src/WorkflowDataProxy.ts | 30 ++++++++--------- tsconfig.build.json | 2 ++ tsconfig.json | 1 + 15 files changed, 33 insertions(+), 93 deletions(-) diff --git a/packages/@n8n/nodes-langchain/.eslintrc.js b/packages/@n8n/nodes-langchain/.eslintrc.js index 1016c4740b225..93091bad23836 100644 --- a/packages/@n8n/nodes-langchain/.eslintrc.js +++ b/packages/@n8n/nodes-langchain/.eslintrc.js @@ -32,7 +32,6 @@ module.exports = { '@typescript-eslint/prefer-nullish-coalescing': 'warn', '@typescript-eslint/no-base-to-string': 'warn', '@typescript-eslint/no-redundant-type-constituents': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-unsafe-argument': 'warn', '@typescript-eslint/prefer-optional-chain': 'warn', '@typescript-eslint/restrict-plus-operands': 'warn', diff --git a/packages/@n8n/nodes-langchain/tsconfig.json b/packages/@n8n/nodes-langchain/tsconfig.json index f031a5ae97426..8377c89500383 100644 --- a/packages/@n8n/nodes-langchain/tsconfig.json +++ b/packages/@n8n/nodes-langchain/tsconfig.json @@ -10,7 +10,6 @@ "forceConsistentCasingInFileNames": true, "noImplicitAny": true, "noImplicitReturns": true, - "noUnusedLocals": true, "strictNullChecks": true, "preserveConstEnums": true, "esModuleInterop": true, diff --git a/packages/@n8n_io/eslint-config/base.js b/packages/@n8n_io/eslint-config/base.js index f5150ee61a69d..4cb51b0cbb2f0 100644 --- a/packages/@n8n_io/eslint-config/base.js +++ b/packages/@n8n_io/eslint-config/base.js @@ -419,19 +419,9 @@ const config = (module.exports = { */ 'prefer-spread': 'error', - /** - * https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-unused-vars.md - */ + // These are tuned off since we use `noUnusedLocals` and `noUnusedParameters` now 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': [ - process.env.CI_LINT_MASTER ? 'warn' : 'error', - { - argsIgnorePattern: '^_', - destructuredArrayIgnorePattern: '^_', - varsIgnorePattern: '^_', - ignoreRestSiblings: true, - }, - ], + '@typescript-eslint/no-unused-vars': 'off', /** * https://www.typescriptlang.org/docs/handbook/enums.html#const-enums @@ -471,12 +461,6 @@ const config = (module.exports = { }, overrides: [ - { - files: ['**/*.d.ts'], - rules: { - '@typescript-eslint/no-unused-vars': 'off', - }, - }, { files: ['test/**/*.ts', '**/__tests__/*.ts'], rules: { @@ -499,7 +483,6 @@ const config = (module.exports = { '@typescript-eslint/no-unsafe-member-access': 'off', '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unused-expressions': 'off', - '@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/prefer-nullish-coalescing': 'off', diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 0e2d2f313d2dc..9eef7ab40af04 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -2,7 +2,6 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ /* eslint-disable id-denylist */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { WorkflowExecute } from 'n8n-core'; @@ -305,11 +304,7 @@ function hookFunctionsPush(): IWorkflowExecuteHooks { }, ], workflowExecuteAfter: [ - async function ( - this: WorkflowHooks, - fullRunData: IRun, - newStaticData: IDataObject, - ): Promise { + async function (this: WorkflowHooks, fullRunData: IRun): Promise { const { sessionId, executionId, retryOf } = this; const { id: workflowId } = this.workflowData; logger.debug('Executing hook (hookFunctionsPush)', { @@ -360,8 +355,7 @@ function hookFunctionsPush(): IWorkflowExecuteHooks { }; } -export function hookFunctionsPreExecute(parentProcessMode?: string): IWorkflowExecuteHooks { - const logger = Container.get(Logger); +export function hookFunctionsPreExecute(): IWorkflowExecuteHooks { const externalHooks = Container.get(ExternalHooks); return { workflowExecuteBefore: [ @@ -556,7 +550,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { }, ], workflowExecuteBefore: [ - async function (workflow: Workflow, data: IRunExecutionData): Promise { + async function (): Promise { void internalHooks.onWorkflowBeforeExecute(this.executionId, this.workflowData); }, ], @@ -625,15 +619,11 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { eventsService.emit('workflowExecutionCompleted', this.workflowData, fullRunData); } }, - async function ( - this: WorkflowHooks, - fullRunData: IRun, - newStaticData: IDataObject, - ): Promise { + async function (this: WorkflowHooks, fullRunData: IRun): Promise { // send tracking and event log events, but don't wait for them void internalHooks.onWorkflowPostExecute(this.executionId, this.workflowData, fullRunData); }, - async function (this: WorkflowHooks, fullRunData: IRun, newStaticData: IDataObject) { + async function (this: WorkflowHooks, fullRunData: IRun) { const externalHooks = Container.get(ExternalHooks); if (externalHooks.exists('workflow.postExecute')) { try { @@ -661,7 +651,6 @@ export async function getRunData( workflowData: IWorkflowBase, userId: string, inputData?: INodeExecutionData[], - parentWorkflowId?: string, ): Promise { const mode = 'integrated'; @@ -1012,7 +1001,7 @@ function getWorkflowHooksIntegrated( ): WorkflowHooks { optionalParameters = optionalParameters || {}; const hookFunctions = hookFunctionsSave(optionalParameters.parentProcessMode); - const preExecuteFunctions = hookFunctionsPreExecute(optionalParameters.parentProcessMode); + const preExecuteFunctions = hookFunctionsPreExecute(); for (const key of Object.keys(preExecuteFunctions)) { if (hookFunctions[key] === undefined) { hookFunctions[key] = []; @@ -1034,7 +1023,7 @@ export function getWorkflowHooksWorkerExecuter( ): WorkflowHooks { optionalParameters = optionalParameters || {}; const hookFunctions = hookFunctionsSaveWorker(); - const preExecuteFunctions = hookFunctionsPreExecute(optionalParameters.parentProcessMode); + const preExecuteFunctions = hookFunctionsPreExecute(); for (const key of Object.keys(preExecuteFunctions)) { if (hookFunctions[key] === undefined) { hookFunctions[key] = []; @@ -1055,7 +1044,7 @@ export function getWorkflowHooksWorkerMain( optionalParameters?: IWorkflowHooksOptionalParameters, ): WorkflowHooks { optionalParameters = optionalParameters || {}; - const hookFunctions = hookFunctionsPreExecute(optionalParameters.parentProcessMode); + const hookFunctions = hookFunctionsPreExecute(); // TODO: why are workers pushing to frontend? // TODO: simplifying this for now to just leave the bare minimum hooks @@ -1075,11 +1064,7 @@ export function getWorkflowHooksWorkerMain( hookFunctions.nodeExecuteBefore = []; hookFunctions.nodeExecuteAfter = []; hookFunctions.workflowExecuteAfter = [ - async function ( - this: WorkflowHooks, - fullRunData: IRun, - newStaticData: IDataObject, - ): Promise { + async function (this: WorkflowHooks, fullRunData: IRun): Promise { const executionStatus = determineFinalExecutionStatus(fullRunData); const saveSettings = toSaveSettings(this.workflowData.settings); diff --git a/packages/cli/src/WorkflowRunnerProcess.ts b/packages/cli/src/WorkflowRunnerProcess.ts index 7ea03f5d7f659..a4c1e9df83397 100644 --- a/packages/cli/src/WorkflowRunnerProcess.ts +++ b/packages/cli/src/WorkflowRunnerProcess.ts @@ -212,7 +212,6 @@ class WorkflowRunnerProcess { workflowData, additionalData.userId, options?.inputData, - options?.parentWorkflowId, ); await sendToParentProcess('startExecution', { runData }); const executionId: string = await new Promise((resolve) => { diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index 955d429586652..fdfeee92b9ccd 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -77,8 +77,7 @@ export class Start extends BaseCommand { private openBrowser() { const editorUrl = Container.get(UrlService).baseUrl; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - open(editorUrl, { wait: true }).catch((error: Error) => { + open(editorUrl, { wait: true }).catch(() => { console.log( `\nWas not able to open URL in browser. Please open manually by visiting:\n${editorUrl}\n`, ); diff --git a/packages/cli/src/config/types.ts b/packages/cli/src/config/types.ts index e6c91b5009a89..eb87e2ca21528 100644 --- a/packages/cli/src/config/types.ts +++ b/packages/cli/src/config/types.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/no-unused-vars */ import type { BinaryData } from 'n8n-core'; import type { schema } from './schema'; diff --git a/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee.ts b/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee.ts index 348eb0ef661d6..3fba5e7e3cc5c 100644 --- a/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee.ts +++ b/packages/cli/src/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unused-vars */ import { MessageEventBusDestination } from './MessageEventBusDestination.ee'; import axios from 'axios'; @@ -133,7 +132,6 @@ export class MessageEventBusDestinationWebhook const sendQuery = this.sendQuery; const specifyQuery = this.specifyQuery; - const sendPayload = this.sendPayload; const sendHeaders = this.sendHeaders; const specifyHeaders = this.specifyHeaders; @@ -287,8 +285,6 @@ export class MessageEventBusDestinationWebhook let httpDigestAuth; let httpHeaderAuth; let httpQueryAuth; - let oAuth1Api; - let oAuth2Api; if (this.authentication === 'genericCredentialType') { if (this.genericAuthType === 'httpBasicAuth') { @@ -307,14 +303,6 @@ export class MessageEventBusDestinationWebhook try { httpQueryAuth = await this.matchDecryptedCredentialType('httpQueryAuth'); } catch {} - } else if (this.genericAuthType === 'oAuth1Api') { - try { - oAuth1Api = await this.matchDecryptedCredentialType('oAuth1Api'); - } catch {} - } else if (this.genericAuthType === 'oAuth2Api') { - try { - oAuth2Api = await this.matchDecryptedCredentialType('oAuth2Api'); - } catch {} } } diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 4398f65fb6d8a..86b8d550e813a 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -13,7 +13,6 @@ "tsBuildInfoFile": "dist/typecheck.tsbuildinfo", // TODO: remove all options below this line "strict": false, - "noUnusedLocals": false, "useUnknownInCatchVariables": false }, "include": ["src/**/*.ts", "test/**/*.ts", "src/sso/saml/saml-schema-metadata-2.0.xsd"], diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index db14c76437bdb..6079fea6347a2 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ /* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/no-unsafe-call */ @@ -2527,8 +2526,6 @@ async function getInputConnectionData( closeFunctions: CloseFunction[], inputName: ConnectionTypes, itemIndex: number, - // TODO: Not implemented yet, and maybe also not needed - inputIndex?: number, ): Promise { const node = this.getNode(); const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion); @@ -3201,12 +3198,12 @@ export function getExecutePollFunctions( return ((workflow: Workflow, node: INode) => { return { ...getCommonWorkflowFunctions(workflow, node, additionalData), - __emit: (data: INodeExecutionData[][]): void => { + __emit: (): void => { throw new ApplicationError( 'Overwrite NodeExecuteFunctions.getExecutePollFunctions.__emit function!', ); }, - __emitError(error: Error) { + __emitError() { throw new ApplicationError( 'Overwrite NodeExecuteFunctions.getExecutePollFunctions.__emitError function!', ); @@ -3264,12 +3261,12 @@ export function getExecuteTriggerFunctions( return ((workflow: Workflow, node: INode) => { return { ...getCommonWorkflowFunctions(workflow, node, additionalData), - emit: (data: INodeExecutionData[][]): void => { + emit: (): void => { throw new ApplicationError( 'Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!', ); }, - emitError: (error: Error): void => { + emitError: (): void => { throw new ApplicationError( 'Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!', ); @@ -3390,8 +3387,6 @@ export function getExecuteFunctions( async getInputConnectionData( inputName: ConnectionTypes, itemIndex: number, - // TODO: Not implemented yet, and maybe also not needed - inputIndex?: number, ): Promise { return await getInputConnectionData.call( this, @@ -3405,7 +3400,6 @@ export function getExecuteFunctions( closeFunctions, inputName, itemIndex, - inputIndex, ); }, @@ -3919,8 +3913,6 @@ export function getExecuteWebhookFunctions( async getInputConnectionData( inputName: ConnectionTypes, itemIndex: number, - // TODO: Not implemented yet, and maybe also not needed - inputIndex?: number, ): Promise { // To be able to use expressions like "$json.sessionId" set the // body data the webhook received to what is normally used for @@ -3954,7 +3946,6 @@ export function getExecuteWebhookFunctions( closeFunctions, inputName, itemIndex, - inputIndex, ); }, getMode: () => mode, diff --git a/packages/node-dev/tsconfig.json b/packages/node-dev/tsconfig.json index bb21c4d964ee9..b3826a84e4384 100644 --- a/packages/node-dev/tsconfig.json +++ b/packages/node-dev/tsconfig.json @@ -5,7 +5,6 @@ "preserveSymlinks": true, "tsBuildInfoFile": "dist/build.tsbuildinfo", // TODO: remove all options below this line - "noUnusedLocals": false, "useUnknownInCatchVariables": false }, "include": ["commands/**/*.ts", "src/**/*.ts"] diff --git a/packages/nodes-base/.eslintrc.js b/packages/nodes-base/.eslintrc.js index 5193ea70b715d..4cd5e580a9541 100644 --- a/packages/nodes-base/.eslintrc.js +++ b/packages/nodes-base/.eslintrc.js @@ -33,7 +33,6 @@ module.exports = { '@typescript-eslint/prefer-nullish-coalescing': 'warn', '@typescript-eslint/no-base-to-string': 'warn', '@typescript-eslint/no-redundant-type-constituents': 'warn', - '@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-unsafe-argument': 'warn', '@typescript-eslint/prefer-optional-chain': 'warn', '@typescript-eslint/restrict-plus-operands': 'warn', diff --git a/packages/workflow/src/WorkflowDataProxy.ts b/packages/workflow/src/WorkflowDataProxy.ts index 96c77452a2f9c..4e8de2a5a6b0d 100644 --- a/packages/workflow/src/WorkflowDataProxy.ts +++ b/packages/workflow/src/WorkflowDataProxy.ts @@ -1,6 +1,4 @@ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */ -/* eslint-disable @typescript-eslint/no-unused-vars */ - /* eslint-disable @typescript-eslint/no-this-alias */ /* eslint-disable @typescript-eslint/no-unsafe-return */ @@ -122,13 +120,13 @@ export class WorkflowDataProxy { return Reflect.ownKeys(target); }, - getOwnPropertyDescriptor(k) { + getOwnPropertyDescriptor() { return { enumerable: true, configurable: true, }; }, - get(target, name, receiver) { + get(_, name) { if (name === 'isProxy') return true; name = name.toString(); @@ -151,7 +149,7 @@ export class WorkflowDataProxy { return Reflect.ownKeys(target); }, - get(target, name, receiver) { + get(_, name) { if (name === 'isProxy') return true; name = name.toString(); return that.selfData[name]; @@ -175,13 +173,13 @@ export class WorkflowDataProxy { ownKeys(target) { return Reflect.ownKeys(target); }, - getOwnPropertyDescriptor(k) { + getOwnPropertyDescriptor() { return { enumerable: true, configurable: true, }; }, - get(target, name, receiver) { + get(target, name) { if (name === 'isProxy') return true; if (name === 'toJSON') return () => deepCopy(target); @@ -439,7 +437,7 @@ export class WorkflowDataProxy { {}, { has: () => true, - get(target, name, receiver) { + get(_, name) { if (name === 'isProxy') return true; if (typeof process === 'undefined') { @@ -470,10 +468,10 @@ export class WorkflowDataProxy { {}, { has: () => true, - ownKeys(target) { + ownKeys() { return allowedValues; }, - getOwnPropertyDescriptor(k) { + getOwnPropertyDescriptor() { return { enumerable: true, configurable: true, @@ -518,10 +516,10 @@ export class WorkflowDataProxy { {}, { has: () => true, - ownKeys(target) { + ownKeys() { return allowedValues; }, - getOwnPropertyDescriptor(k) { + getOwnPropertyDescriptor() { return { enumerable: true, configurable: true, @@ -561,7 +559,7 @@ export class WorkflowDataProxy { {}, { has: () => true, - get(target, name, receiver) { + get(_, name) { if (name === 'isProxy') return true; const nodeName = name.toString(); @@ -932,7 +930,7 @@ export class WorkflowDataProxy { {}, { has: () => true, - ownKeys(target) { + ownKeys() { return [ 'pairedItem', 'itemMatching', @@ -1056,10 +1054,10 @@ export class WorkflowDataProxy { $input: new Proxy({} as ProxyInput, { has: () => true, - ownKeys(target) { + ownKeys() { return ['all', 'context', 'first', 'item', 'last', 'params']; }, - getOwnPropertyDescriptor(k) { + getOwnPropertyDescriptor() { return { enumerable: true, configurable: true, diff --git a/tsconfig.build.json b/tsconfig.build.json index 11866f14e134d..8a1a0105e38c7 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -2,6 +2,8 @@ "compilerOptions": { "types": ["node"], "noEmit": false, + "noUnusedLocals": false, + "noUnusedParameters": false, "declaration": true }, "tsc-alias": { diff --git a/tsconfig.json b/tsconfig.json index f37b693003677..1b0bd25650b62 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "noImplicitAny": true, "noImplicitReturns": true, "noUnusedLocals": true, + "noUnusedParameters": true, "strictNullChecks": true, "preserveConstEnums": true, "esModuleInterop": true, From dafacb90c6b01e2f88c3de359ebb2d8d55e0aecc Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Thu, 25 Jan 2024 19:11:23 +0200 Subject: [PATCH 4/6] fix(Merge Node): Passing on no items to "Input 2" results in wrong output items (#8438) --- .../test/node/workflow.keep_non_matches.json | 588 ++++++++++++++++++ .../nodes-base/nodes/Merge/v2/MergeV2.node.ts | 5 + 2 files changed, 593 insertions(+) create mode 100644 packages/nodes-base/nodes/Merge/test/node/workflow.keep_non_matches.json diff --git a/packages/nodes-base/nodes/Merge/test/node/workflow.keep_non_matches.json b/packages/nodes-base/nodes/Merge/test/node/workflow.keep_non_matches.json new file mode 100644 index 0000000000000..8c2f85552a14b --- /dev/null +++ b/packages/nodes-base/nodes/Merge/test/node/workflow.keep_non_matches.json @@ -0,0 +1,588 @@ +{ + "name": "merge fix", + "nodes": [ + { + "parameters": {}, + "id": "94003e55-6c4e-492f-802a-49f4fb5b5f4b", + "name": "When clicking \"Test Workflow\"", + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 120, + 940 + ] + }, + { + "parameters": { + "jsCode": "return [\n {\n \"id\": 1,\n \"first_name\": \"John\",\n \"last_name\": \"Doe\"\n },\n {\n \"id\": 2,\n \"first_name\": \"Jane\",\n \"last_name\": \"Smith\"\n },\n {\n \"id\": 3,\n \"first_name\": \"Michael\",\n \"last_name\": \"Johnson\"\n },\n {\n \"id\": 4,\n \"first_name\": \"Emily\",\n \"last_name\": \"Davis\"\n },\n {\n \"id\": 5,\n \"first_name\": \"Robert\",\n \"last_name\": \"Taylor\"\n },\n {\n \"id\": 6,\n \"first_name\": \"Olivia\",\n \"last_name\": \"Martin\"\n },\n {\n \"id\": 7,\n \"first_name\": \"William\",\n \"last_name\": \"Brown\"\n },\n {\n \"id\": 8,\n \"first_name\": \"Sophia\",\n \"last_name\": \"Anderson\"\n },\n {\n \"id\": 9,\n \"first_name\": \"Daniel\",\n \"last_name\": \"Wilson\"\n },\n {\n \"id\": 10,\n \"first_name\": \"Ava\",\n \"last_name\": \"Miller\"\n }\n];" + }, + "id": "da82fbb0-b689-4c4e-b310-303361e8411c", + "name": "Mock data A", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 340, + 840 + ] + }, + { + "parameters": { + "jsCode": "return [\n];" + }, + "id": "493ecb02-52e7-43b7-8c73-d45a3f7e76f6", + "name": "Mock data B", + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 340, + 1040 + ] + }, + { + "parameters": { + "mode": "combine", + "mergeByFields": { + "values": [ + { + "field1": "id", + "field2": "id" + } + ] + }, + "joinMode": "keepNonMatches", + "options": {} + }, + "id": "d0c0f123-1461-4b78-9cf8-6410440430d3", + "name": "Merge", + "type": "n8n-nodes-base.merge", + "typeVersion": 2.1, + "position": [ + 700, + 500 + ] + }, + { + "parameters": { + "mode": "combine", + "mergeByFields": { + "values": [ + { + "field1": "id", + "field2": "id" + } + ] + }, + "joinMode": "keepNonMatches", + "options": {} + }, + "id": "5093ec3e-c987-44c7-815f-279ea7842db5", + "name": "Merge1", + "type": "n8n-nodes-base.merge", + "typeVersion": 2.1, + "position": [ + 700, + 680 + ] + }, + { + "parameters": { + "mode": "combine", + "mergeByFields": { + "values": [ + { + "field1": "id", + "field2": "id" + } + ] + }, + "joinMode": "keepNonMatches", + "outputDataFrom": "input1", + "options": {} + }, + "id": "0453bb35-3971-4a23-93b7-31853e4634fb", + "name": "Merge2", + "type": "n8n-nodes-base.merge", + "typeVersion": 2.1, + "position": [ + 700, + 880 + ] + }, + { + "parameters": { + "mode": "combine", + "mergeByFields": { + "values": [ + { + "field1": "id", + "field2": "id" + } + ] + }, + "joinMode": "keepNonMatches", + "outputDataFrom": "input1", + "options": {} + }, + "id": "f09c9327-4557-406b-8ae0-db851055dd08", + "name": "Merge3", + "type": "n8n-nodes-base.merge", + "typeVersion": 2.1, + "position": [ + 700, + 1060 + ], + "alwaysOutputData": true + }, + { + "parameters": { + "mode": "combine", + "mergeByFields": { + "values": [ + { + "field1": "id", + "field2": "id" + } + ] + }, + "joinMode": "keepNonMatches", + "outputDataFrom": "input2", + "options": {} + }, + "id": "d5bc6ee7-141e-4eba-a9aa-cb866b177b89", + "name": "Merge4", + "type": "n8n-nodes-base.merge", + "typeVersion": 2.1, + "position": [ + 700, + 1280 + ], + "alwaysOutputData": true + }, + { + "parameters": { + "mode": "combine", + "mergeByFields": { + "values": [ + { + "field1": "id", + "field2": "id" + } + ] + }, + "joinMode": "keepNonMatches", + "outputDataFrom": "input2", + "options": {} + }, + "id": "4c881679-3f76-491a-a2c9-e35e4c9e28ff", + "name": "Merge5", + "type": "n8n-nodes-base.merge", + "typeVersion": 2.1, + "position": [ + 700, + 1460 + ], + "alwaysOutputData": true + } + ], + "pinData": { + "Merge": [ + { + "json": { + "id": 1, + "first_name": "John", + "last_name": "Doe" + } + }, + { + "json": { + "id": 2, + "first_name": "Jane", + "last_name": "Smith" + } + }, + { + "json": { + "id": 3, + "first_name": "Michael", + "last_name": "Johnson" + } + }, + { + "json": { + "id": 4, + "first_name": "Emily", + "last_name": "Davis" + } + }, + { + "json": { + "id": 5, + "first_name": "Robert", + "last_name": "Taylor" + } + }, + { + "json": { + "id": 6, + "first_name": "Olivia", + "last_name": "Martin" + } + }, + { + "json": { + "id": 7, + "first_name": "William", + "last_name": "Brown" + } + }, + { + "json": { + "id": 8, + "first_name": "Sophia", + "last_name": "Anderson" + } + }, + { + "json": { + "id": 9, + "first_name": "Daniel", + "last_name": "Wilson" + } + }, + { + "json": { + "id": 10, + "first_name": "Ava", + "last_name": "Miller" + } + } + ], + "Merge1": [ + { + "json": { + "id": 1, + "first_name": "John", + "last_name": "Doe" + } + }, + { + "json": { + "id": 2, + "first_name": "Jane", + "last_name": "Smith" + } + }, + { + "json": { + "id": 3, + "first_name": "Michael", + "last_name": "Johnson" + } + }, + { + "json": { + "id": 4, + "first_name": "Emily", + "last_name": "Davis" + } + }, + { + "json": { + "id": 5, + "first_name": "Robert", + "last_name": "Taylor" + } + }, + { + "json": { + "id": 6, + "first_name": "Olivia", + "last_name": "Martin" + } + }, + { + "json": { + "id": 7, + "first_name": "William", + "last_name": "Brown" + } + }, + { + "json": { + "id": 8, + "first_name": "Sophia", + "last_name": "Anderson" + } + }, + { + "json": { + "id": 9, + "first_name": "Daniel", + "last_name": "Wilson" + } + }, + { + "json": { + "id": 10, + "first_name": "Ava", + "last_name": "Miller" + } + } + ], + "Merge2": [ + { + "json": { + "id": 1, + "first_name": "John", + "last_name": "Doe" + } + }, + { + "json": { + "id": 2, + "first_name": "Jane", + "last_name": "Smith" + } + }, + { + "json": { + "id": 3, + "first_name": "Michael", + "last_name": "Johnson" + } + }, + { + "json": { + "id": 4, + "first_name": "Emily", + "last_name": "Davis" + } + }, + { + "json": { + "id": 5, + "first_name": "Robert", + "last_name": "Taylor" + } + }, + { + "json": { + "id": 6, + "first_name": "Olivia", + "last_name": "Martin" + } + }, + { + "json": { + "id": 7, + "first_name": "William", + "last_name": "Brown" + } + }, + { + "json": { + "id": 8, + "first_name": "Sophia", + "last_name": "Anderson" + } + }, + { + "json": { + "id": 9, + "first_name": "Daniel", + "last_name": "Wilson" + } + }, + { + "json": { + "id": 10, + "first_name": "Ava", + "last_name": "Miller" + } + } + ], + "Merge3": [ + { + "json": {} + } + ], + "Merge4": [ + { + "json": {} + } + ], + "Merge5": [ + { + "json": { + "id": 1, + "first_name": "John", + "last_name": "Doe" + } + }, + { + "json": { + "id": 2, + "first_name": "Jane", + "last_name": "Smith" + } + }, + { + "json": { + "id": 3, + "first_name": "Michael", + "last_name": "Johnson" + } + }, + { + "json": { + "id": 4, + "first_name": "Emily", + "last_name": "Davis" + } + }, + { + "json": { + "id": 5, + "first_name": "Robert", + "last_name": "Taylor" + } + }, + { + "json": { + "id": 6, + "first_name": "Olivia", + "last_name": "Martin" + } + }, + { + "json": { + "id": 7, + "first_name": "William", + "last_name": "Brown" + } + }, + { + "json": { + "id": 8, + "first_name": "Sophia", + "last_name": "Anderson" + } + }, + { + "json": { + "id": 9, + "first_name": "Daniel", + "last_name": "Wilson" + } + }, + { + "json": { + "id": 10, + "first_name": "Ava", + "last_name": "Miller" + } + } + ] + }, + "connections": { + "When clicking \"Test Workflow\"": { + "main": [ + [ + { + "node": "Mock data A", + "type": "main", + "index": 0 + }, + { + "node": "Mock data B", + "type": "main", + "index": 0 + } + ] + ] + }, + "Mock data A": { + "main": [ + [ + { + "node": "Merge", + "type": "main", + "index": 0 + }, + { + "node": "Merge1", + "type": "main", + "index": 1 + }, + { + "node": "Merge2", + "type": "main", + "index": 0 + }, + { + "node": "Merge3", + "type": "main", + "index": 1 + }, + { + "node": "Merge4", + "type": "main", + "index": 0 + }, + { + "node": "Merge5", + "type": "main", + "index": 1 + } + ] + ] + }, + "Mock data B": { + "main": [ + [ + { + "node": "Merge", + "type": "main", + "index": 1 + }, + { + "node": "Merge1", + "type": "main", + "index": 0 + }, + { + "node": "Merge2", + "type": "main", + "index": 1 + }, + { + "node": "Merge3", + "type": "main", + "index": 0 + }, + { + "node": "Merge4", + "type": "main", + "index": 1 + }, + { + "node": "Merge5", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1" + }, + "versionId": "54329e46-4d05-491a-919c-e156e77d6d11", + "meta": { + "templateCredsSetupCompleted": true, + "instanceId": "b888bd11cd1ddbb95450babf3e199556799d999b896f650de768b8370ee50363" + }, + "id": "WbgpQNak2tO6spx2", + "tags": [] +} diff --git a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts index 2df877965818b..5ade42d061ba0 100644 --- a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts +++ b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts @@ -485,6 +485,11 @@ export class MergeV2 implements INodeType { } if (input1.length === 0 || input2.length === 0) { + if (!input1.length && joinMode === 'keepNonMatches' && outputDataFrom === 'input1') + return [returnData]; + if (!input2.length && joinMode === 'keepNonMatches' && outputDataFrom === 'input2') + return [returnData]; + if (joinMode === 'keepMatches') { // Stop the execution return [[]]; From caab97e667df5d305aa1d2e15c0d31eb5f1a84eb Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Fri, 26 Jan 2024 06:54:49 +0100 Subject: [PATCH 5/6] fix(editor): Show pin button on binary output but disable it with tooltip (#8388) --- packages/editor-ui/src/components/RunData.vue | 66 +++------- .../src/components/RunDataPinButton.vue | 68 ++++++++++ .../__tests__/RunDataPinButton.test.ts | 124 ++++++++++++++++++ .../src/plugins/i18n/locales/en.json | 1 + 4 files changed, 212 insertions(+), 47 deletions(-) create mode 100644 packages/editor-ui/src/components/RunDataPinButton.vue create mode 100644 packages/editor-ui/src/components/__tests__/RunDataPinButton.test.ts diff --git a/packages/editor-ui/src/components/RunData.vue b/packages/editor-ui/src/components/RunData.vue index 212027ff3bb54..1cd3dec8d6f2b 100644 --- a/packages/editor-ui/src/components/RunData.vue +++ b/packages/editor-ui/src/components/RunData.vue @@ -70,48 +70,24 @@ data-test-id="ndv-edit-pinned-data" @click="enterEditMode({ origin: 'editIconButton' })" /> - - - - - + :tooltip-contents-visibility="{ + binaryDataTooltipContent: !!binaryData?.length, + pinDataDiscoveryTooltipContent: + isControlledPinDataTooltip && pinDataDiscoveryTooltipVisible, + }" + :data-pinning-docs-url="dataPinningDocsUrl" + :pinned-data="pinnedData" + @toggle-pin-data="onTogglePinData({ source: 'pin-icon-click' })" + />
await import('@/components/RunDataTable.vue'), @@ -649,6 +626,7 @@ export default defineComponent({ RunDataSchema, RunDataHtml, RunDataSearch, + RunDataPinButton, }, props: { node: { @@ -1727,12 +1705,6 @@ export default defineComponent({ max-width: 240px; } -.pinDataButton { - svg { - transition: transform 0.3s ease; - } -} - .spinner { * { color: var(--color-primary); diff --git a/packages/editor-ui/src/components/RunDataPinButton.vue b/packages/editor-ui/src/components/RunDataPinButton.vue new file mode 100644 index 0000000000000..9fd3539515c18 --- /dev/null +++ b/packages/editor-ui/src/components/RunDataPinButton.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/packages/editor-ui/src/components/__tests__/RunDataPinButton.test.ts b/packages/editor-ui/src/components/__tests__/RunDataPinButton.test.ts new file mode 100644 index 0000000000000..1e6b7958c8364 --- /dev/null +++ b/packages/editor-ui/src/components/__tests__/RunDataPinButton.test.ts @@ -0,0 +1,124 @@ +import { createTestingPinia } from '@pinia/testing'; +import { cleanup, waitFor } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; +import { createComponentRenderer } from '@/__tests__/render'; +import RunDataPinButton from '@/components/RunDataPinButton.vue'; +import { STORES } from '@/constants'; + +const renderComponent = createComponentRenderer(RunDataPinButton, { + global: { + stubs: ['font-awesome-icon'], + plugins: [ + createTestingPinia({ + initialState: { + [STORES.SETTINGS]: { + settings: { + templates: { + enabled: true, + host: 'https://api.n8n.io/api/', + }, + }, + }, + }, + }), + ], + }, + props: { + tooltipContentsVisibility: { + binaryDataTooltipContent: false, + pinDataDiscoveryTooltipContent: false, + }, + dataPinningDocsUrl: '', + pinnedData: { + hasData: false, + }, + disabled: false, + }, +}); + +describe('RunDataPinButton.vue', () => { + beforeEach(cleanup); + + it('shows default tooltip content only on button hover', async () => { + const { getByRole, queryByRole, emitted } = renderComponent(); + + expect(queryByRole('tooltip')).not.toBeInTheDocument(); + + expect(getByRole('button')).toBeEnabled(); + await userEvent.hover(getByRole('button')); + + expect(getByRole('tooltip')).toBeVisible(); + expect(getByRole('tooltip')).toHaveTextContent('More info'); + + await userEvent.click(getByRole('button')); + expect(emitted().togglePinData).toBeDefined(); + }); + + it('shows binary data tooltip content only on disabled button hover', async () => { + const { getByRole, queryByRole, emitted } = renderComponent({ + props: { + tooltipContentsVisibility: { + binaryDataTooltipContent: true, + pinDataDiscoveryTooltipContent: false, + }, + disabled: true, + }, + }); + + expect(queryByRole('tooltip')).not.toBeInTheDocument(); + expect(getByRole('button')).toBeDisabled(); + + await userEvent.hover(getByRole('button')); + + expect(getByRole('tooltip')).toBeVisible(); + expect(getByRole('tooltip')).toHaveTextContent('disabled'); + + await userEvent.click(getByRole('button')); + expect(emitted().togglePinData).not.toBeDefined(); + }); + + it('shows pin data discoverability tooltip immediately (not on hover)', async () => { + const { getByRole } = renderComponent({ + props: { + tooltipContentsVisibility: { + binaryDataTooltipContent: false, + pinDataDiscoveryTooltipContent: true, + }, + }, + }); + + await waitFor(() => { + expect(getByRole('tooltip')).toBeVisible(); + expect(getByRole('tooltip')).toHaveTextContent('instead of waiting'); + }); + expect(getByRole('button')).toBeEnabled(); + + await userEvent.hover(getByRole('button')); + + expect(getByRole('tooltip')).toBeVisible(); + expect(getByRole('tooltip')).toHaveTextContent('instead of waiting'); + }); + + it('shows binary data tooltip content even if discoverability tooltip enabled', async () => { + const { getByRole } = renderComponent({ + props: { + tooltipContentsVisibility: { + binaryDataTooltipContent: true, + pinDataDiscoveryTooltipContent: true, + }, + disabled: true, + }, + }); + + await waitFor(() => { + expect(getByRole('tooltip')).toBeVisible(); + expect(getByRole('tooltip')).toHaveTextContent('disabled'); + }); + expect(getByRole('button')).toBeDisabled(); + + await userEvent.hover(getByRole('button')); + + expect(getByRole('tooltip')).toBeVisible(); + expect(getByRole('tooltip')).toHaveTextContent('disabled'); + }); +}); diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 3db1094d1cd34..b8bf30503ad54 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -820,6 +820,7 @@ "ndv.title.renameNode": "Rename node", "ndv.pinData.pin.title": "Pin data", "ndv.pinData.pin.description": "Node will always output this data instead of executing.", + "ndv.pinData.pin.binary": "Pin Data is disabled as this node's output contains binary data.", "ndv.pinData.pin.link": "More info", "ndv.pinData.pin.multipleRuns.title": "Run #{index} was pinned", "ndv.pinData.pin.multipleRuns.description": "This run will be outputted each time the node is run.", From ad4b298be34915718b0415322e328a1b46ef5842 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Fri, 26 Jan 2024 06:55:08 +0100 Subject: [PATCH 6/6] fix(editor): Fix workflows filter resetting (#8411) --- .../src/__tests__/server/endpoints/index.ts | 4 + .../src/__tests__/server/endpoints/tag.ts | 11 +++ .../__tests__/server/endpoints/workflow.ts | 16 +++ .../src/__tests__/server/factories/index.ts | 4 + .../src/__tests__/server/factories/tag.ts | 12 +++ .../__tests__/server/factories/workflow.ts | 18 ++++ .../src/__tests__/server/fixtures/index.ts | 7 ++ .../src/__tests__/server/fixtures/tags.ts | 15 +++ .../__tests__/server/fixtures/workflows.ts | 47 +++++++++ .../editor-ui/src/__tests__/server/index.ts | 3 + .../src/__tests__/server/models/index.ts | 4 + .../src/__tests__/server/models/tag.ts | 5 + .../src/__tests__/server/models/workflow.ts | 5 + .../layouts/ResourcesListLayout.vue | 1 + .../src/views/__tests__/WorkflowsView.test.ts | 99 +++++++++++++++++++ 15 files changed, 251 insertions(+) create mode 100644 packages/editor-ui/src/__tests__/server/endpoints/tag.ts create mode 100644 packages/editor-ui/src/__tests__/server/endpoints/workflow.ts create mode 100644 packages/editor-ui/src/__tests__/server/factories/tag.ts create mode 100644 packages/editor-ui/src/__tests__/server/factories/workflow.ts create mode 100644 packages/editor-ui/src/__tests__/server/fixtures/index.ts create mode 100644 packages/editor-ui/src/__tests__/server/fixtures/tags.ts create mode 100644 packages/editor-ui/src/__tests__/server/fixtures/workflows.ts create mode 100644 packages/editor-ui/src/__tests__/server/models/tag.ts create mode 100644 packages/editor-ui/src/__tests__/server/models/workflow.ts create mode 100644 packages/editor-ui/src/views/__tests__/WorkflowsView.test.ts diff --git a/packages/editor-ui/src/__tests__/server/endpoints/index.ts b/packages/editor-ui/src/__tests__/server/endpoints/index.ts index edc073f2a0c67..93a48d3dfec6d 100644 --- a/packages/editor-ui/src/__tests__/server/endpoints/index.ts +++ b/packages/editor-ui/src/__tests__/server/endpoints/index.ts @@ -6,6 +6,8 @@ import { routesForVariables } from './variable'; import { routesForSettings } from './settings'; import { routesForSSO } from './sso'; import { routesForSourceControl } from './sourceControl'; +import { routesForWorkflows } from './workflow'; +import { routesForTags } from './tag'; const endpoints: Array<(server: Server) => void> = [ routesForCredentials, @@ -15,6 +17,8 @@ const endpoints: Array<(server: Server) => void> = [ routesForSettings, routesForSSO, routesForSourceControl, + routesForWorkflows, + routesForTags, ]; export { endpoints }; diff --git a/packages/editor-ui/src/__tests__/server/endpoints/tag.ts b/packages/editor-ui/src/__tests__/server/endpoints/tag.ts new file mode 100644 index 0000000000000..7891c20262b6d --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/endpoints/tag.ts @@ -0,0 +1,11 @@ +import type { Server } from 'miragejs'; +import { Response } from 'miragejs'; +import type { AppSchema } from '../types'; + +export function routesForTags(server: Server) { + server.get('/rest/tags', (schema: AppSchema) => { + const { models: data } = schema.all('tag'); + + return new Response(200, {}, { data }); + }); +} diff --git a/packages/editor-ui/src/__tests__/server/endpoints/workflow.ts b/packages/editor-ui/src/__tests__/server/endpoints/workflow.ts new file mode 100644 index 0000000000000..4418cdb061377 --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/endpoints/workflow.ts @@ -0,0 +1,16 @@ +import type { Server } from 'miragejs'; +import { Response } from 'miragejs'; +import type { AppSchema } from '../types'; + +export function routesForWorkflows(server: Server) { + server.get('/rest/workflows', (schema: AppSchema) => { + const { models: data } = schema.all('workflow'); + + return new Response(200, {}, { data }); + }); + server.get('/rest/active-workflows', (schema: AppSchema) => { + const { models: data } = schema.all('workflow'); + + return new Response(200, {}, { data }); + }); +} diff --git a/packages/editor-ui/src/__tests__/server/factories/index.ts b/packages/editor-ui/src/__tests__/server/factories/index.ts index 9b5155c28f1e8..77de915bad3a5 100644 --- a/packages/editor-ui/src/__tests__/server/factories/index.ts +++ b/packages/editor-ui/src/__tests__/server/factories/index.ts @@ -2,6 +2,8 @@ import { userFactory } from './user'; import { credentialFactory } from './credential'; import { credentialTypeFactory } from './credentialType'; import { variableFactory } from './variable'; +import { workflowFactory } from './workflow'; +import { tagFactory } from './tag'; export * from './user'; export * from './credential'; @@ -13,4 +15,6 @@ export const factories = { credentialType: credentialTypeFactory, user: userFactory, variable: variableFactory, + workflow: workflowFactory, + tag: tagFactory, }; diff --git a/packages/editor-ui/src/__tests__/server/factories/tag.ts b/packages/editor-ui/src/__tests__/server/factories/tag.ts new file mode 100644 index 0000000000000..bed3ca227679d --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/factories/tag.ts @@ -0,0 +1,12 @@ +import { Factory } from 'miragejs'; +import type { ITag } from '@/Interface'; +import { faker } from '@faker-js/faker'; + +export const tagFactory = Factory.extend({ + id(i: string) { + return i; + }, + name() { + return faker.lorem.word(); + }, +}); diff --git a/packages/editor-ui/src/__tests__/server/factories/workflow.ts b/packages/editor-ui/src/__tests__/server/factories/workflow.ts new file mode 100644 index 0000000000000..6d12a8958b180 --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/factories/workflow.ts @@ -0,0 +1,18 @@ +import { Factory } from 'miragejs'; +import type { IWorkflowDb } from '@/Interface'; +import { faker } from '@faker-js/faker'; + +export const workflowFactory = Factory.extend({ + id(i: string) { + return i; + }, + name() { + return faker.lorem.word(); + }, + createdAt() { + return faker.date.recent().toISOString(); + }, + tags() { + return faker.lorem.words(2.5).split(' '); + }, +}); diff --git a/packages/editor-ui/src/__tests__/server/fixtures/index.ts b/packages/editor-ui/src/__tests__/server/fixtures/index.ts new file mode 100644 index 0000000000000..12f59798817da --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/fixtures/index.ts @@ -0,0 +1,7 @@ +import { tags } from './tags'; +import { workflows } from './workflows'; + +export const fixtures = { + tags, + workflows, +}; diff --git a/packages/editor-ui/src/__tests__/server/fixtures/tags.ts b/packages/editor-ui/src/__tests__/server/fixtures/tags.ts new file mode 100644 index 0000000000000..740acef117a84 --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/fixtures/tags.ts @@ -0,0 +1,15 @@ +import type { ITag } from '@/Interface'; +export const tags: ITag[] = [ + { + id: '1', + name: 'tag1', + }, + { + id: '2', + name: 'tag2', + }, + { + id: '3', + name: 'tag3', + }, +]; diff --git a/packages/editor-ui/src/__tests__/server/fixtures/workflows.ts b/packages/editor-ui/src/__tests__/server/fixtures/workflows.ts new file mode 100644 index 0000000000000..68e05e0d9da6f --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/fixtures/workflows.ts @@ -0,0 +1,47 @@ +import type { IWorkflowDb } from '@/Interface'; +import { faker } from '@faker-js/faker'; + +export const workflows = [ + { + id: '1', + name: 'workflow1', + tags: [], + }, + { + id: '2', + name: 'workflow2', + tags: [ + { id: '1', name: 'tag1' }, + { id: '2', name: 'tag2' }, + ], + }, + { + id: '3', + name: 'workflow3', + tags: [ + { id: '1', name: 'tag1' }, + { id: '3', name: 'tag3' }, + ], + }, + { + id: '4', + name: 'workflow4', + tags: [ + { id: '2', name: 'tag2' }, + { id: '3', name: 'tag3' }, + ], + }, + { + id: '5', + name: 'workflow5', + tags: [ + { id: '1', name: 'tag1' }, + { id: '2', name: 'tag2' }, + { id: '3', name: 'tag3' }, + ], + }, +].map((wf, idx) => ({ + ...wf, + createdAt: faker.date.recent().toISOString(), + updatedAt: new Date(`2024-1-${idx + 1}`).toISOString(), +})) as IWorkflowDb[]; diff --git a/packages/editor-ui/src/__tests__/server/index.ts b/packages/editor-ui/src/__tests__/server/index.ts index 18ea0a4be8b67..ef8d4e7a23cbf 100644 --- a/packages/editor-ui/src/__tests__/server/index.ts +++ b/packages/editor-ui/src/__tests__/server/index.ts @@ -2,12 +2,15 @@ import { createServer } from 'miragejs'; import { endpoints } from './endpoints'; import { models } from './models'; import { factories } from './factories'; +import { fixtures } from './fixtures'; export function setupServer() { const server = createServer({ models, factories, + fixtures, seeds(server) { + server.loadFixtures('tags', 'workflows'); server.createList('credentialType', 8); server.create('user', { firstName: 'Nathan', diff --git a/packages/editor-ui/src/__tests__/server/models/index.ts b/packages/editor-ui/src/__tests__/server/models/index.ts index 6b9f39327b9bf..f947fb9c531c1 100644 --- a/packages/editor-ui/src/__tests__/server/models/index.ts +++ b/packages/editor-ui/src/__tests__/server/models/index.ts @@ -2,10 +2,14 @@ import { UserModel } from './user'; import { CredentialModel } from './credential'; import { CredentialTypeModel } from './credentialType'; import { VariableModel } from './variable'; +import { WorkflowModel } from './workflow'; +import { TagModel } from './tag'; export const models = { credential: CredentialModel, credentialType: CredentialTypeModel, user: UserModel, variable: VariableModel, + workflow: WorkflowModel, + tag: TagModel, }; diff --git a/packages/editor-ui/src/__tests__/server/models/tag.ts b/packages/editor-ui/src/__tests__/server/models/tag.ts new file mode 100644 index 0000000000000..737e2288562c0 --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/models/tag.ts @@ -0,0 +1,5 @@ +import type { ITag } from '@/Interface'; +import { Model } from 'miragejs'; +import type { ModelDefinition } from 'miragejs/-types'; + +export const TagModel: ModelDefinition = Model.extend({}); diff --git a/packages/editor-ui/src/__tests__/server/models/workflow.ts b/packages/editor-ui/src/__tests__/server/models/workflow.ts new file mode 100644 index 0000000000000..7be4f100035df --- /dev/null +++ b/packages/editor-ui/src/__tests__/server/models/workflow.ts @@ -0,0 +1,5 @@ +import type { IWorkflowDb } from '@/Interface'; +import { Model } from 'miragejs'; +import type { ModelDefinition } from 'miragejs/-types'; + +export const WorkflowModel: ModelDefinition = Model.extend({}); diff --git a/packages/editor-ui/src/components/layouts/ResourcesListLayout.vue b/packages/editor-ui/src/components/layouts/ResourcesListLayout.vue index 47e7a9a4c7882..a3d932b13da4c 100644 --- a/packages/editor-ui/src/components/layouts/ResourcesListLayout.vue +++ b/packages/editor-ui/src/components/layouts/ResourcesListLayout.vue @@ -454,6 +454,7 @@ export default defineComponent({ this.resettingFilters = true; this.sendFiltersTelemetry('reset'); + this.$emit('update:filters', this.filtersModel); }, focusSearchInput() { if (this.$refs.search) { diff --git a/packages/editor-ui/src/views/__tests__/WorkflowsView.test.ts b/packages/editor-ui/src/views/__tests__/WorkflowsView.test.ts new file mode 100644 index 0000000000000..9adbd8c637238 --- /dev/null +++ b/packages/editor-ui/src/views/__tests__/WorkflowsView.test.ts @@ -0,0 +1,99 @@ +import { afterAll, beforeAll } from 'vitest'; +import { setActivePinia, createPinia } from 'pinia'; +import { waitFor } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; +import { setupServer } from '@/__tests__/server'; +import WorkflowsView from '@/views/WorkflowsView.vue'; +import { useSettingsStore } from '@/stores/settings.store'; +import { useUsersStore } from '@/stores/users.store'; +import { createComponentRenderer } from '@/__tests__/render'; + +const originalOffsetHeight = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + 'offsetHeight', +) as PropertyDescriptor; + +describe('WorkflowsView', () => { + let server: ReturnType; + let pinia: ReturnType; + let settingsStore: ReturnType; + let usersStore: ReturnType; + + const renderComponent = createComponentRenderer(WorkflowsView, { + global: { + mocks: { + $route: { + query: {}, + }, + $router: { + replace: vi.fn(), + }, + }, + }, + }); + + beforeAll(() => { + Object.defineProperties(HTMLElement.prototype, { + offsetHeight: { + get() { + return this.getAttribute('data-test-id') === 'resources-list' ? 1000 : 100; + }, + }, + }); + + server = setupServer(); + }); + + afterAll(() => { + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight); + }); + + beforeEach(async () => { + pinia = createPinia(); + setActivePinia(pinia); + + settingsStore = useSettingsStore(); + usersStore = useUsersStore(); + await settingsStore.getSettings(); + await usersStore.fetchUsers(); + await usersStore.loginWithCookie(); + }); + + afterAll(() => { + server.shutdown(); + }); + + it('should filter workflows by tags', async () => { + const { container, getByTestId, getAllByTestId, queryByTestId } = renderComponent({ + pinia, + }); + + expect(container.querySelectorAll('.n8n-loading')).toHaveLength(3); + expect(queryByTestId('resources-list')).not.toBeInTheDocument(); + + await waitFor(() => { + expect(container.querySelectorAll('.n8n-loading')).toHaveLength(0); + // There are 5 workflows defined in server fixtures + expect(getAllByTestId('resources-list-item')).toHaveLength(5); + }); + + await userEvent.click( + getAllByTestId('resources-list-item')[0].querySelector('.n8n-tag') as HTMLElement, + ); + await waitFor(() => { + expect(getAllByTestId('resources-list-item').length).toBeLessThan(5); + }); + + await userEvent.click(getByTestId('workflows-filter-reset')); + await waitFor(() => { + expect(getAllByTestId('resources-list-item')).toHaveLength(5); + }); + + await userEvent.click( + getAllByTestId('resources-list-item')[3].querySelector('.n8n-tag') as HTMLElement, + ); + await waitFor(() => { + expect(getAllByTestId('resources-list-item').length).toBeLessThan(5); + }); + }); +});