From 0a3d04faa2df645a16e5d15a38f184602ed88311 Mon Sep 17 00:00:00 2001 From: Matsu Date: Wed, 3 Jun 2026 09:08:31 +0300 Subject: [PATCH] test: Migrate @n8n/workflow-sdk from Jest to Vitest (no-changelog) (#31546) Co-authored-by: Claude Opus 4.8 (1M context) --- packages/@n8n/workflow-sdk/jest.config.js | 5 -- packages/@n8n/workflow-sdk/package.json | 11 ++- .../workflow-sdk/scripts/extract-workflows.ts | 2 +- ...global-setup.ts => vitest-global-setup.ts} | 6 +- .../src/ast-interpreter/interpreter.test.ts | 82 ++++++++++--------- .../src/codegen/code-generator.test.ts | 1 - .../src/codegen/composite-builder.test.ts | 2 - .../composite-handlers/error-handler.test.ts | 2 - .../if-else-handler.test.ts | 2 - .../composite-handlers/merge-handler.test.ts | 2 - .../composite-handlers/sib-handler.test.ts | 2 - .../switch-case-handler.test.ts | 2 - .../src/codegen/config-builder.test.ts | 2 - .../src/codegen/constants.test.ts | 2 - .../src/codegen/deferred-connections.test.ts | 2 - .../codegen/execution-schema-jsdoc.test.ts | 1 - .../src/codegen/execution-status.test.ts | 1 - .../src/codegen/expression-annotator.test.ts | 2 - .../src/codegen/graph-annotator.test.ts | 2 - .../workflow-sdk/src/codegen/index.test.ts | 2 - .../src/codegen/new-codegen-roundtrip.test.ts | 1 - .../src/codegen/node-type-utils.test.ts | 2 - .../src/codegen/semantic-graph.test.ts | 2 - .../src/codegen/semantic-registry.test.ts | 2 - .../src/codegen/string-utils.test.ts | 2 - .../src/codegen/subnode-generator.test.ts | 1 - .../src/codegen/variable-names.test.ts | 2 - .../generate-node-defs-cli.test.ts | 6 +- .../workflow-sdk/src/pin-data-utils.test.ts | 12 +-- .../workflow-sdk/src/types/aliases.test.ts | 12 +-- .../src/utils/code-helpers.test.ts | 6 +- .../src/validation/test-schema-setup.ts | 7 +- .../src/workflow-builder-plugins.test.ts | 20 ++--- .../src/workflow-builder/constants.test.ts | 2 - .../branch-handler-utils.test.ts | 22 ++--- .../if-else-handler.test.ts | 4 +- .../split-in-batches-handler.test.ts | 18 ++-- .../switch-case-handler.test.ts | 4 +- .../workflow-builder/plugins/registry.test.ts | 15 ++-- .../src/workflow-builder/string-utils.test.ts | 2 - .../validation-helpers.test.ts | 2 - packages/@n8n/workflow-sdk/tsconfig.json | 2 +- packages/@n8n/workflow-sdk/vitest.config.ts | 7 ++ pnpm-lock.yaml | 9 ++ 44 files changed, 133 insertions(+), 162 deletions(-) delete mode 100644 packages/@n8n/workflow-sdk/jest.config.js rename packages/@n8n/workflow-sdk/scripts/{jest-global-setup.ts => vitest-global-setup.ts} (67%) create mode 100644 packages/@n8n/workflow-sdk/vitest.config.ts diff --git a/packages/@n8n/workflow-sdk/jest.config.js b/packages/@n8n/workflow-sdk/jest.config.js deleted file mode 100644 index 7d0b53ebc78..00000000000 --- a/packages/@n8n/workflow-sdk/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('jest').Config} */ -module.exports = { - ...require('../../../jest.config'), - globalSetup: '/scripts/jest-global-setup.ts', -}; diff --git a/packages/@n8n/workflow-sdk/package.json b/packages/@n8n/workflow-sdk/package.json index f16c6dfb77b..68699a2472a 100644 --- a/packages/@n8n/workflow-sdk/package.json +++ b/packages/@n8n/workflow-sdk/package.json @@ -50,9 +50,9 @@ "lint:fix": "eslint . --fix", "watch": "tsc -p tsconfig.build.json --watch", "extract-workflows": "npx tsx scripts/extract-workflows.ts", - "test": "jest", - "test:unit": "jest", - "test:dev": "jest --watch", + "test": "vitest run", + "test:unit": "vitest run", + "test:dev": "vitest --silent=false", "generate-types": "npx tsx src/generate-types/generate-types.ts", "fetch-workflows": "npx tsx scripts/fetch-test-workflows.ts", "create-workflows-zip": "npx tsx scripts/create-workflows-zip.ts", @@ -87,9 +87,12 @@ "devDependencies": { "@n8n/eslint-plugin-community-nodes": "workspace:*", "@n8n/typescript-config": "workspace:*", + "@n8n/vitest-config": "workspace:*", "@types/adm-zip": "^0.5.7", "@types/estree": "^1.0.8", - "adm-zip": "^0.5.16" + "@vitest/coverage-v8": "catalog:", + "adm-zip": "^0.5.16", + "vitest": "catalog:" }, "dependencies": { "@dagrejs/dagre": "^1.1.4", diff --git a/packages/@n8n/workflow-sdk/scripts/extract-workflows.ts b/packages/@n8n/workflow-sdk/scripts/extract-workflows.ts index c4adc47ddeb..ce2eb75b070 100644 --- a/packages/@n8n/workflow-sdk/scripts/extract-workflows.ts +++ b/packages/@n8n/workflow-sdk/scripts/extract-workflows.ts @@ -3,7 +3,7 @@ * * test-fixtures/real-workflows/public_published_templates.zip → test-fixtures/real-workflows/ * - * Runs automatically via Jest's globalSetup (see scripts/jest-global-setup.ts). + * Runs automatically via Vitest's globalSetup (see scripts/vitest-global-setup.ts). * * Usage: * npx tsx scripts/extract-workflows.ts diff --git a/packages/@n8n/workflow-sdk/scripts/jest-global-setup.ts b/packages/@n8n/workflow-sdk/scripts/vitest-global-setup.ts similarity index 67% rename from packages/@n8n/workflow-sdk/scripts/jest-global-setup.ts rename to packages/@n8n/workflow-sdk/scripts/vitest-global-setup.ts index a5f1e841ca6..ddbd4528cba 100644 --- a/packages/@n8n/workflow-sdk/scripts/jest-global-setup.ts +++ b/packages/@n8n/workflow-sdk/scripts/vitest-global-setup.ts @@ -1,10 +1,10 @@ /** - * Jest globalSetup hook: extract committed zips into the expected on-disk + * Vitest globalSetup hook: extract committed zips into the expected on-disk * layout before any test runs. We can't rely on npm/pnpm's pretest hook here * because CI invokes `test:unit` (turbo task), not `test`. */ import { extractAllWorkflows } from './extract-workflows'; -module.exports = () => { +export default function setup() { extractAllWorkflows(); -}; +} diff --git a/packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.test.ts b/packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.test.ts index faa6d33849d..d859a4d7a76 100644 --- a/packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.test.ts +++ b/packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.test.ts @@ -1,6 +1,8 @@ /** * Unit tests for the AST interpreter. */ +import type { Mock } from 'vitest'; + import { InterpreterError, SecurityError, @@ -13,71 +15,71 @@ import { interpretSDKCode } from './interpreter'; import { parseSDKCode } from './parser'; /** Helper to get the first call argument from a Jest mock with proper typing */ -function getFirstCallArg(mockFn: jest.Mock): T { +function getFirstCallArg(mockFn: Mock): T { const calls = mockFn.mock.calls as unknown[][]; return calls[0][0] as T; } // Mock SDK functions for testing const createMockSDKFunctions = (): SDKFunctions => ({ - workflow: jest.fn((id: string, name: string) => ({ + workflow: vi.fn((id: string, name: string) => ({ id, name, nodes: [] as unknown[], - add: jest.fn(function (this: { nodes: unknown[] }, node: unknown) { + add: vi.fn(function (this: { nodes: unknown[] }, node: unknown) { this.nodes.push(node); return this; }), - then: jest.fn(function (this: { nodes: unknown[] }, node: unknown) { + then: vi.fn(function (this: { nodes: unknown[] }, node: unknown) { this.nodes.push(node); return this; }), - toJSON: jest.fn(function (this: { id: string; name: string; nodes: unknown[] }) { + toJSON: vi.fn(function (this: { id: string; name: string; nodes: unknown[] }) { return { id: this.id, name: this.name, nodes: this.nodes }; }), })), - node: jest.fn((config: unknown) => ({ + node: vi.fn((config: unknown) => ({ type: 'node', config, - then: jest.fn((target: unknown) => target), - to: jest.fn((target: unknown) => target), - input: jest.fn(() => ({ index: 0 })), - output: jest.fn(() => ({ index: 0 })), - onError: jest.fn(), + then: vi.fn((target: unknown) => target), + to: vi.fn((target: unknown) => target), + input: vi.fn(() => ({ index: 0 })), + output: vi.fn(() => ({ index: 0 })), + onError: vi.fn(), })), - trigger: jest.fn((config: unknown) => ({ + trigger: vi.fn((config: unknown) => ({ type: 'trigger', config, - then: jest.fn((target: unknown) => target), - to: jest.fn((target: unknown) => target), + then: vi.fn((target: unknown) => target), + to: vi.fn((target: unknown) => target), })), - sticky: jest.fn((content: string, options?: unknown) => ({ + sticky: vi.fn((content: string, options?: unknown) => ({ type: 'sticky', content, options, })), - placeholder: jest.fn((value: string) => `<__PLACEHOLDER_VALUE__${value}__>`), - newCredential: jest.fn((name: string) => ({ __newCredential: true, name })), - ifElse: jest.fn(), - switchCase: jest.fn(), - merge: jest.fn((config: unknown) => ({ type: 'merge', config, input: jest.fn() })), - splitInBatches: jest.fn(), - nextBatch: jest.fn(), - languageModel: jest.fn((config: unknown) => ({ type: 'languageModel', config })), - memory: jest.fn((config: unknown) => ({ type: 'memory', config })), - tool: jest.fn((config: unknown) => ({ type: 'tool', config })), - outputParser: jest.fn((config: unknown) => ({ type: 'outputParser', config })), - embedding: jest.fn((config: unknown) => ({ type: 'embedding', config })), - embeddings: jest.fn((config: unknown) => ({ type: 'embeddings', config })), - vectorStore: jest.fn((config: unknown) => ({ type: 'vectorStore', config })), - retriever: jest.fn((config: unknown) => ({ type: 'retriever', config })), - documentLoader: jest.fn((config: unknown) => ({ type: 'documentLoader', config })), - textSplitter: jest.fn((config: unknown) => ({ type: 'textSplitter', config })), - reranker: jest.fn((config: unknown) => ({ type: 'reranker', config })), - fromAi: jest.fn( + placeholder: vi.fn((value: string) => `<__PLACEHOLDER_VALUE__${value}__>`), + newCredential: vi.fn((name: string) => ({ __newCredential: true, name })), + ifElse: vi.fn(), + switchCase: vi.fn(), + merge: vi.fn((config: unknown) => ({ type: 'merge', config, input: vi.fn() })), + splitInBatches: vi.fn(), + nextBatch: vi.fn(), + languageModel: vi.fn((config: unknown) => ({ type: 'languageModel', config })), + memory: vi.fn((config: unknown) => ({ type: 'memory', config })), + tool: vi.fn((config: unknown) => ({ type: 'tool', config })), + outputParser: vi.fn((config: unknown) => ({ type: 'outputParser', config })), + embedding: vi.fn((config: unknown) => ({ type: 'embedding', config })), + embeddings: vi.fn((config: unknown) => ({ type: 'embeddings', config })), + vectorStore: vi.fn((config: unknown) => ({ type: 'vectorStore', config })), + retriever: vi.fn((config: unknown) => ({ type: 'retriever', config })), + documentLoader: vi.fn((config: unknown) => ({ type: 'documentLoader', config })), + textSplitter: vi.fn((config: unknown) => ({ type: 'textSplitter', config })), + reranker: vi.fn((config: unknown) => ({ type: 'reranker', config })), + fromAi: vi.fn( (key: string, desc?: string) => `={{ $fromAI('${key}'${desc ? `, '${desc}'` : ''}) }}`, ), - nodeJson: jest.fn((node: { name: string } | string, path: string) => { + nodeJson: vi.fn((node: { name: string } | string, path: string) => { const name = typeof node === 'string' ? node : node.name; return `={{ $('${name}').item.json.${path} }}`; }), @@ -101,7 +103,7 @@ describe('AST Interpreter', () => { const code = 'const x = {;'; try { parseSDKCode(code); - fail('Should have thrown'); + expect.fail('Should have thrown'); } catch (error) { expect(error).toBeInstanceOf(InterpreterError); expect((error as InterpreterError).location).toBeDefined(); @@ -714,7 +716,7 @@ describe('AST Interpreter', () => { // Verify node was called with the subnode expect(sdkFunctions.node).toHaveBeenCalled(); const nodeCallArgs = getFirstCallArg<{ config: { subnodes: { model: unknown } } }>( - sdkFunctions.node as jest.Mock, + sdkFunctions.node as Mock, ); expect(nodeCallArgs.config.subnodes.model).toBeDefined(); }); @@ -760,7 +762,7 @@ describe('AST Interpreter', () => { // Verify tool was called with the fromAi result expect(sdkFunctions.tool).toHaveBeenCalled(); const toolCallArgs = getFirstCallArg<{ config: { parameters: { sendTo: string } } }>( - sdkFunctions.tool as jest.Mock, + sdkFunctions.tool as Mock, ); expect(toolCallArgs.config.parameters.sendTo).toContain('$fromAI'); }); @@ -805,8 +807,8 @@ describe('AST Interpreter', () => { }); it('should allow connect() method on workflow builder', () => { - const connectMock = jest.fn(); - sdkFunctions.workflow = jest.fn(() => ({ + const connectMock = vi.fn(); + sdkFunctions.workflow = vi.fn(() => ({ connect: connectMock, })); const code = ` diff --git a/packages/@n8n/workflow-sdk/src/codegen/code-generator.test.ts b/packages/@n8n/workflow-sdk/src/codegen/code-generator.test.ts index 16d1b05fac1..696e526d140 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/code-generator.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/code-generator.test.ts @@ -1,4 +1,3 @@ -import { describe, it, expect, beforeAll } from '@jest/globals'; import * as fs from 'fs'; import * as path from 'path'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/composite-builder.test.ts b/packages/@n8n/workflow-sdk/src/codegen/composite-builder.test.ts index d77e9afcce8..16a5214e06f 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/composite-builder.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/composite-builder.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildCompositeTree } from './composite-builder'; import type { LeafNode, diff --git a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/error-handler.test.ts b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/error-handler.test.ts index 01e9b8ac2a5..1bd54fbca5a 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/error-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/error-handler.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildErrorHandler, hasErrorOutput, getErrorOutputTargets } from './error-handler'; import type { OnError } from '../../types/base'; import type { CompositeNode, LeafNode } from '../composite-tree'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/if-else-handler.test.ts b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/if-else-handler.test.ts index 1559e654300..b7113a929fb 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/if-else-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/if-else-handler.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildIfElseComposite, type BuildContext } from './if-else-handler'; import type { CompositeNode, LeafNode } from '../composite-tree'; import type { SemanticGraph, SemanticNode } from '../types'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/merge-handler.test.ts b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/merge-handler.test.ts index b60909a3891..1f6968c99d9 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/merge-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/merge-handler.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildMergeComposite, type BuildContext } from './merge-handler'; import type { CompositeNode } from '../composite-tree'; import type { SemanticGraph, SemanticNode, SourceInfo } from '../types'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/sib-handler.test.ts b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/sib-handler.test.ts index def34aa6df1..027a2af34af 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/sib-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/sib-handler.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildSplitInBatchesComposite, type BuildContext } from './sib-handler'; import type { CompositeNode, LeafNode } from '../composite-tree'; import type { SemanticGraph, SemanticNode, SourceInfo } from '../types'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/switch-case-handler.test.ts b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/switch-case-handler.test.ts index 5c669f97bc5..e28a575467c 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/switch-case-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/composite-handlers/switch-case-handler.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildSwitchCaseComposite, type BuildContext } from './switch-case-handler'; import type { CompositeNode, LeafNode } from '../composite-tree'; import type { SemanticGraph, SemanticNode } from '../types'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/config-builder.test.ts b/packages/@n8n/workflow-sdk/src/codegen/config-builder.test.ts index 263ee207ee0..1651b11b8f4 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/config-builder.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/config-builder.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildConfigString, type ConfigEntry } from './config-builder'; describe('buildConfigString', () => { diff --git a/packages/@n8n/workflow-sdk/src/codegen/constants.test.ts b/packages/@n8n/workflow-sdk/src/codegen/constants.test.ts index f66bc52bb33..a24b565f6c5 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/constants.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/constants.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { AI_CONNECTION_TO_CONFIG_KEY, AI_CONNECTION_TO_BUILDER, diff --git a/packages/@n8n/workflow-sdk/src/codegen/deferred-connections.test.ts b/packages/@n8n/workflow-sdk/src/codegen/deferred-connections.test.ts index 34a01959731..d16fa7e85ff 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/deferred-connections.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/deferred-connections.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import type { CompositeNode } from './composite-tree'; import { createDeferredConnection, diff --git a/packages/@n8n/workflow-sdk/src/codegen/execution-schema-jsdoc.test.ts b/packages/@n8n/workflow-sdk/src/codegen/execution-schema-jsdoc.test.ts index b414016a745..854b1a8b2fc 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/execution-schema-jsdoc.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/execution-schema-jsdoc.test.ts @@ -1,4 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; import type { Schema } from 'n8n-workflow'; import { generateSchemaJSDoc, schemaToOutputSample } from './execution-schema-jsdoc'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/execution-status.test.ts b/packages/@n8n/workflow-sdk/src/codegen/execution-status.test.ts index be0b0688ac3..33b248555a6 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/execution-status.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/execution-status.test.ts @@ -1,4 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; import type { IRunExecutionData } from 'n8n-workflow'; import { buildNodeExecutionStatus, formatExecutionStatusJSDoc } from './execution-status'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/expression-annotator.test.ts b/packages/@n8n/workflow-sdk/src/codegen/expression-annotator.test.ts index f8514fb65ae..1fc80e6749a 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/expression-annotator.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/expression-annotator.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildExpressionAnnotations } from './expression-annotator'; import type { ExpressionValue } from './types'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/graph-annotator.test.ts b/packages/@n8n/workflow-sdk/src/codegen/graph-annotator.test.ts index 4c3ab17ef08..ca89895e18f 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/graph-annotator.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/graph-annotator.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { annotateGraph } from './graph-annotator'; import { buildSemanticGraph } from './semantic-graph'; import type { WorkflowJSON } from '../types/base'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/index.test.ts b/packages/@n8n/workflow-sdk/src/codegen/index.test.ts index ff29030da3c..bd336b63237 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/index.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/index.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { generateWorkflowCode } from './index'; import type { WorkflowJSON } from '../types/base'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/new-codegen-roundtrip.test.ts b/packages/@n8n/workflow-sdk/src/codegen/new-codegen-roundtrip.test.ts index 811a2944acb..e124a7dcc3c 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/new-codegen-roundtrip.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/new-codegen-roundtrip.test.ts @@ -2,7 +2,6 @@ * Roundtrip tests using the NEW codegen implementation. * This tests if the new codegen can replace the old one. */ -import { describe, it, expect } from '@jest/globals'; import { generateWorkflowCode } from './index'; import { parseWorkflowCode } from './parse-workflow-code'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/node-type-utils.test.ts b/packages/@n8n/workflow-sdk/src/codegen/node-type-utils.test.ts index ea0d6a2f9f4..2b65af0f4cb 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/node-type-utils.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/node-type-utils.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { isTriggerType, isStickyNote, diff --git a/packages/@n8n/workflow-sdk/src/codegen/semantic-graph.test.ts b/packages/@n8n/workflow-sdk/src/codegen/semantic-graph.test.ts index f2802bce22b..58f45e648ba 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/semantic-graph.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/semantic-graph.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { buildSemanticGraph } from './semantic-graph'; import type { WorkflowJSON } from '../types/base'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/semantic-registry.test.ts b/packages/@n8n/workflow-sdk/src/codegen/semantic-registry.test.ts index 7e78e230d71..83284051c54 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/semantic-registry.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/semantic-registry.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { getOutputName, getInputName, diff --git a/packages/@n8n/workflow-sdk/src/codegen/string-utils.test.ts b/packages/@n8n/workflow-sdk/src/codegen/string-utils.test.ts index 98a586b75a6..0cd2204fe56 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/string-utils.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/string-utils.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { escapeString, needsQuoting, formatKey, escapeRegexChars } from './string-utils'; describe('string-utils', () => { diff --git a/packages/@n8n/workflow-sdk/src/codegen/subnode-generator.test.ts b/packages/@n8n/workflow-sdk/src/codegen/subnode-generator.test.ts index 7c101cd6907..2bfa8460c1f 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/subnode-generator.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/subnode-generator.test.ts @@ -1,4 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; import type { IDataObject } from 'n8n-workflow'; import { generateSubnodeCall, generateSubnodesConfig, formatValue } from './subnode-generator'; diff --git a/packages/@n8n/workflow-sdk/src/codegen/variable-names.test.ts b/packages/@n8n/workflow-sdk/src/codegen/variable-names.test.ts index e629f040c0e..de653509115 100644 --- a/packages/@n8n/workflow-sdk/src/codegen/variable-names.test.ts +++ b/packages/@n8n/workflow-sdk/src/codegen/variable-names.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { RESERVED_KEYWORDS, toVarName, getVarName, getUniqueVarName } from './variable-names'; describe('variable-names', () => { diff --git a/packages/@n8n/workflow-sdk/src/generate-types/generate-node-defs-cli.test.ts b/packages/@n8n/workflow-sdk/src/generate-types/generate-node-defs-cli.test.ts index 7a0b6c89369..f5a1e9adce4 100644 --- a/packages/@n8n/workflow-sdk/src/generate-types/generate-node-defs-cli.test.ts +++ b/packages/@n8n/workflow-sdk/src/generate-types/generate-node-defs-cli.test.ts @@ -1,7 +1,7 @@ /** * Tests for generate-node-defs-cli * - * Run with: cd packages/@n8n/workflow-sdk && pnpm jest generate-node-defs-cli + * Run with: cd packages/@n8n/workflow-sdk && pnpm test generate-node-defs-cli */ import * as fs from 'fs'; @@ -108,7 +108,7 @@ describe('generate-node-defs-cli', () => { await new Promise((resolve) => setTimeout(resolve, 50)); // Second run: should skip (files unchanged) - const consoleSpy = jest.spyOn(console, 'log'); + const consoleSpy = vi.spyOn(console, 'log'); await generateNodeDefinitions({ nodesJsonPath, outputDir }); expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('up to date')); @@ -162,7 +162,7 @@ describe('generate-node-defs-cli', () => { await fs.promises.unlink(hashFile); // Should regenerate (not skip) - const consoleSpy = jest.spyOn(console, 'log'); + const consoleSpy = vi.spyOn(console, 'log'); await generateNodeDefinitions({ nodesJsonPath, outputDir }); const logCalls = consoleSpy.mock.calls.map((c) => String(c[0])); diff --git a/packages/@n8n/workflow-sdk/src/pin-data-utils.test.ts b/packages/@n8n/workflow-sdk/src/pin-data-utils.test.ts index 704b44f376f..98a742f30f0 100644 --- a/packages/@n8n/workflow-sdk/src/pin-data-utils.test.ts +++ b/packages/@n8n/workflow-sdk/src/pin-data-utils.test.ts @@ -9,11 +9,11 @@ import { } from './pin-data-utils'; // Mock the generate-types module used by discoverOutputSchemaForNode -const mockDiscoverSchemasForNode = jest.fn(); -const mockFindSchemaForOperation = jest.fn(); -const mockGenerateJsonSchemaFromData = jest.fn(); +const mockDiscoverSchemasForNode = vi.fn(); +const mockFindSchemaForOperation = vi.fn(); +const mockGenerateJsonSchemaFromData = vi.fn(); -jest.mock('./generate-types', () => ({ +vi.mock('./generate-types', () => ({ discoverSchemasForNode: (...args: unknown[]) => mockDiscoverSchemasForNode(...args) as unknown, findSchemaForOperation: (...args: unknown[]) => mockFindSchemaForOperation(...args) as unknown, generateJsonSchemaFromData: (...args: unknown[]) => @@ -84,7 +84,7 @@ describe('needsPinData', () => { describe('discoverOutputSchemaForNode', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('returns undefined for empty node type', () => { @@ -148,7 +148,7 @@ describe('discoverOutputSchemaForNode', () => { describe('inferSchemasFromRunData', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('returns empty object for empty run data', () => { diff --git a/packages/@n8n/workflow-sdk/src/types/aliases.test.ts b/packages/@n8n/workflow-sdk/src/types/aliases.test.ts index 5d0cd34f899..7776e302326 100644 --- a/packages/@n8n/workflow-sdk/src/types/aliases.test.ts +++ b/packages/@n8n/workflow-sdk/src/types/aliases.test.ts @@ -17,13 +17,13 @@ describe('Type Aliases', () => { id: 'test', name: 'Test', config: {}, - then: jest.fn(), - to: jest.fn(), - input: jest.fn(), - output: jest.fn(), - onError: jest.fn(), + then: vi.fn(), + to: vi.fn(), + input: vi.fn(), + output: vi.fn(), + onError: vi.fn(), getConnections: () => [], - update: jest.fn(), + update: vi.fn(), } as unknown as AnyNode; expect(node.type).toBe('n8n-nodes-base.set'); diff --git a/packages/@n8n/workflow-sdk/src/utils/code-helpers.test.ts b/packages/@n8n/workflow-sdk/src/utils/code-helpers.test.ts index 09ec531e271..2446065f8cc 100644 --- a/packages/@n8n/workflow-sdk/src/utils/code-helpers.test.ts +++ b/packages/@n8n/workflow-sdk/src/utils/code-helpers.test.ts @@ -110,9 +110,9 @@ describe('Code Node Helpers', () => { return [{ json: { data: ctx('Config').json } }]; }); - // ctx('Config') should become $('Config') - expect(result.jsCode).toContain("$('Config')"); - expect(result.jsCode).not.toContain("ctx('Config')"); + // ctx('Config') should become $('Config') (quote style depends on the transpiler) + expect(result.jsCode).toMatch(/\$\(['"]Config['"]\)/); + expect(result.jsCode).not.toMatch(/ctx\(['"]Config['"]\)/); }); }); }); diff --git a/packages/@n8n/workflow-sdk/src/validation/test-schema-setup.ts b/packages/@n8n/workflow-sdk/src/validation/test-schema-setup.ts index f8b69cac0a8..6f4ae1252b5 100644 --- a/packages/@n8n/workflow-sdk/src/validation/test-schema-setup.ts +++ b/packages/@n8n/workflow-sdk/src/validation/test-schema-setup.ts @@ -14,10 +14,13 @@ import * as path from 'path'; import { setSchemaBaseDirs, getSchemaBaseDirs } from './schema-validator'; import { generateNodeDefinitions } from '../generate-types/generate-node-defs-cli'; -// Use a worker-specific directory to prevent race conditions when multiple Jest +// Use a worker-specific directory to prevent race conditions when multiple test // workers run schema-using tests in parallel (they would otherwise concurrently // delete and regenerate schemas into the same shared temp directory). -const WORKER_ID = process.env.JEST_WORKER_ID ?? '0'; +// Vitest exposes the per-worker id as VITEST_POOL_ID; Jest used JEST_WORKER_ID. +// Without this, every parallel worker collapses onto the same `-0` directory and +// one worker's rmSync wipes schemas another worker just generated. +const WORKER_ID = process.env.VITEST_POOL_ID ?? process.env.JEST_WORKER_ID ?? '0'; const SCHEMA_TEST_DIR = path.join(os.tmpdir(), `n8n-schema-tests-${WORKER_ID}`); const STAMP_FILE = path.join(SCHEMA_TEST_DIR, '.generator-hash'); diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts index b078867edae..63d616152e6 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder-plugins.test.ts @@ -39,7 +39,7 @@ function createMockValidator( id, name: `Mock Validator ${id}`, nodeTypes, - validateNode: jest.fn(validateNodeFn), + validateNode: vi.fn(validateNodeFn), }; } @@ -52,7 +52,7 @@ describe('WorkflowBuilder plugin integration', () => { describe('validate() with plugins', () => { it('runs registered validators for matching node types', () => { - const mockValidateNode = jest.fn().mockReturnValue([]); + const mockValidateNode = vi.fn().mockReturnValue([]); const mockValidator = createMockValidator( 'test:mock', ['n8n-nodes-base.set'], @@ -301,7 +301,7 @@ describe('WorkflowBuilder plugin integration', () => { describe('add() with composite handlers', () => { it('delegates IfElseComposite to registered handler when handler handles it', () => { - const mockAddNodes = jest.fn().mockReturnValue('If Node'); + const mockAddNodes = vi.fn().mockReturnValue('If Node'); const mockHandler: CompositeHandlerPlugin = { id: 'test:if-else', name: 'Test If/Else Handler', @@ -403,7 +403,7 @@ describe('WorkflowBuilder plugin integration', () => { describe('then() with composite handlers', () => { it('delegates IfElseComposite to registered handler in then()', () => { - const mockAddNodes = jest + const mockAddNodes = vi .fn() .mockImplementation((input: IfElseComposite, ctx: MutablePluginContext) => { ctx.addNodeWithSubnodes(input.ifNode); @@ -442,7 +442,7 @@ describe('WorkflowBuilder plugin integration', () => { // Import the global registry to spy on it // Spy on the global registry's findCompositeHandler method - const findCompositeHandlerSpy = jest.spyOn(pluginRegistry, 'findCompositeHandler'); + const findCompositeHandlerSpy = vi.spyOn(pluginRegistry, 'findCompositeHandler'); // Create a workflow WITHOUT explicitly passing a registry // Use ifElse composite which should trigger findCompositeHandler @@ -473,7 +473,7 @@ describe('WorkflowBuilder plugin integration', () => { }); it('uses global pluginRegistry.findCompositeHandler in then() when no registry is provided', () => { - const findCompositeHandlerSpy = jest.spyOn(pluginRegistry, 'findCompositeHandler'); + const findCompositeHandlerSpy = vi.spyOn(pluginRegistry, 'findCompositeHandler'); const startTrigger = trigger({ type: 'n8n-nodes-base.manualTrigger', @@ -613,7 +613,7 @@ describe('WorkflowBuilder plugin integration', () => { it('ifElse builder is handled by global pluginRegistry handler', () => { registerDefaultPlugins(pluginRegistry); - const findHandlerSpy = jest.spyOn(pluginRegistry, 'findCompositeHandler'); + const findHandlerSpy = vi.spyOn(pluginRegistry, 'findCompositeHandler'); const trueBranch = node({ type: 'n8n-nodes-base.set', @@ -646,7 +646,7 @@ describe('WorkflowBuilder plugin integration', () => { it('switchCase builder is handled by global pluginRegistry handler', () => { registerDefaultPlugins(pluginRegistry); - const findHandlerSpy = jest.spyOn(pluginRegistry, 'findCompositeHandler'); + const findHandlerSpy = vi.spyOn(pluginRegistry, 'findCompositeHandler'); const case0 = node({ type: 'n8n-nodes-base.set', @@ -679,7 +679,7 @@ describe('WorkflowBuilder plugin integration', () => { it('splitInBatches builder is handled by global pluginRegistry handler', () => { registerDefaultPlugins(pluginRegistry); - const findHandlerSpy = jest.spyOn(pluginRegistry, 'findCompositeHandler'); + const findHandlerSpy = vi.spyOn(pluginRegistry, 'findCompositeHandler'); const doneNode = node({ type: 'n8n-nodes-base.set', @@ -774,7 +774,7 @@ describe('WorkflowBuilder plugin integration', () => { }); it('handler.getHeadNodeName is called by resolveCompositeHeadName', () => { - const mockGetHeadNodeName = jest.fn().mockReturnValue('Custom Head'); + const mockGetHeadNodeName = vi.fn().mockReturnValue('Custom Head'); const customHandler: CompositeHandlerPlugin<{ custom: true }> = { id: 'test:custom', name: 'Custom Handler', diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/constants.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/constants.test.ts index 3b3d9641ed5..22bf7130415 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/constants.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/constants.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { GRID_SIZE, DEFAULT_NODE_SIZE, diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/branch-handler-utils.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/branch-handler-utils.test.ts index c76b3c6ba98..8e7c77127ae 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/branch-handler-utils.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/branch-handler-utils.test.ts @@ -58,20 +58,20 @@ describe('branch-handler-utils', () => { describe('collectFromTarget', () => { it('should not call collector for null', () => { - const collector = jest.fn(); + const collector = vi.fn(); collectFromTarget(null, collector); expect(collector).not.toHaveBeenCalled(); }); it('should not call collector for undefined', () => { - const collector = jest.fn(); + const collector = vi.fn(); collectFromTarget(undefined, collector); expect(collector).not.toHaveBeenCalled(); }); it('should call collector for single node', () => { const node = createMockNode('TestNode'); - const collector = jest.fn(); + const collector = vi.fn(); collectFromTarget(node, collector); expect(collector).toHaveBeenCalledWith(node); }); @@ -79,7 +79,7 @@ describe('branch-handler-utils', () => { it('should call collector for each node in array', () => { const node1 = createMockNode('Node1'); const node2 = createMockNode('Node2'); - const collector = jest.fn(); + const collector = vi.fn(); collectFromTarget([node1, node2], collector); expect(collector).toHaveBeenCalledTimes(2); expect(collector).toHaveBeenCalledWith(node1); @@ -88,7 +88,7 @@ describe('branch-handler-utils', () => { it('should skip null elements in array', () => { const node1 = createMockNode('Node1'); - const collector = jest.fn(); + const collector = vi.fn(); collectFromTarget([node1, null, null], collector); expect(collector).toHaveBeenCalledTimes(1); expect(collector).toHaveBeenCalledWith(node1); @@ -98,7 +98,7 @@ describe('branch-handler-utils', () => { describe('addBranchTargetNodes', () => { it('should do nothing for null', () => { const ctx = { - addBranchToGraph: jest.fn(), + addBranchToGraph: vi.fn(), } as unknown as MutablePluginContext; addBranchTargetNodes(null, ctx); expect(ctx.addBranchToGraph).not.toHaveBeenCalled(); @@ -107,7 +107,7 @@ describe('branch-handler-utils', () => { it('should call addBranchToGraph for single target', () => { const node = createMockNode('TestNode'); const ctx = { - addBranchToGraph: jest.fn(), + addBranchToGraph: vi.fn(), } as unknown as MutablePluginContext; addBranchTargetNodes(node, ctx); expect(ctx.addBranchToGraph).toHaveBeenCalledWith(node); @@ -117,7 +117,7 @@ describe('branch-handler-utils', () => { const node1 = createMockNode('Node1'); const node2 = createMockNode('Node2'); const ctx = { - addBranchToGraph: jest.fn(), + addBranchToGraph: vi.fn(), } as unknown as MutablePluginContext; addBranchTargetNodes([node1, node2], ctx); expect(ctx.addBranchToGraph).toHaveBeenCalledTimes(2); @@ -128,7 +128,7 @@ describe('branch-handler-utils', () => { it('should skip null branch', () => { const mainConns = new Map(); const ctx = { - addBranchToGraph: jest.fn(), + addBranchToGraph: vi.fn(), } as unknown as MutablePluginContext; processBranchForComposite(null, 0, ctx, mainConns); expect(mainConns.size).toBe(0); @@ -138,7 +138,7 @@ describe('branch-handler-utils', () => { const node = createMockNode('TestNode'); const mainConns = new Map(); const ctx = { - addBranchToGraph: jest.fn().mockReturnValue('TestNode'), + addBranchToGraph: vi.fn().mockReturnValue('TestNode'), } as unknown as MutablePluginContext; processBranchForComposite(node, 0, ctx, mainConns); expect(mainConns.get(0)).toEqual([{ node: 'TestNode', type: 'main', index: 0 }]); @@ -149,7 +149,7 @@ describe('branch-handler-utils', () => { const node2 = createMockNode('Node2'); const mainConns = new Map(); const ctx = { - addBranchToGraph: jest.fn().mockImplementation((n: { name: string }) => n.name), + addBranchToGraph: vi.fn().mockImplementation((n: { name: string }) => n.name), } as unknown as MutablePluginContext; processBranchForComposite([node1, node2], 0, ctx, mainConns); expect(mainConns.get(0)).toEqual([ diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/if-else-handler.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/if-else-handler.test.ts index 82b73952f59..a3e04c5b587 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/if-else-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/if-else-handler.test.ts @@ -53,14 +53,14 @@ function createMockContext(): MutablePluginContext { workflowId: 'test-workflow', workflowName: 'Test Workflow', settings: {}, - addNodeWithSubnodes: jest.fn((node: NodeInstance) => { + addNodeWithSubnodes: vi.fn((node: NodeInstance) => { nodes.set(node.name, { instance: node, connections: new Map(), }); return node.name; }), - addBranchToGraph: jest.fn((branch: unknown) => { + addBranchToGraph: vi.fn((branch: unknown) => { const branchNode = branch as NodeInstance; nodes.set(branchNode.name, { instance: branchNode, diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/split-in-batches-handler.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/split-in-batches-handler.test.ts index c8f1c74fad4..fefbaf83768 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/split-in-batches-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/split-in-batches-handler.test.ts @@ -68,14 +68,14 @@ function createMockContext(): MutablePluginContext { workflowId: 'test-workflow', workflowName: 'Test Workflow', settings: {}, - addNodeWithSubnodes: jest.fn((node: NodeInstance) => { + addNodeWithSubnodes: vi.fn((node: NodeInstance) => { nodes.set(node.name, { instance: node, connections: new Map(), }); return node.name; }), - addBranchToGraph: jest.fn((branch: unknown) => { + addBranchToGraph: vi.fn((branch: unknown) => { const branchNode = branch as NodeInstance; nodes.set(branchNode.name, { instance: branchNode, @@ -284,7 +284,7 @@ describe('splitInBatchesHandler', () => { // Mock addBranchToGraph to call addNodes recursively (simulating nested SIB) let recursiveCallCount = 0; - ctx.addBranchToGraph = jest.fn((branch: unknown) => { + ctx.addBranchToGraph = vi.fn((branch: unknown) => { const branchNode = branch as NodeInstance; ctx.nodes.set(branchNode.name, { instance: branchNode, @@ -390,7 +390,7 @@ describe('splitInBatchesHandler', () => { workflowId: 'test-workflow', workflowName: 'Test Workflow', settings: {}, - addNodeWithSubnodes: jest.fn((node: NodeInstance) => { + addNodeWithSubnodes: vi.fn((node: NodeInstance) => { nodes.set(node.name, { instance: node, connections: new Map>([ @@ -399,7 +399,7 @@ describe('splitInBatchesHandler', () => { }); return node.name; }), - addBranchToGraph: jest.fn((branch: unknown) => { + addBranchToGraph: vi.fn((branch: unknown) => { const branchNode = branch as NodeInstance; if (!nodes.has(branchNode.name)) { nodes.set(branchNode.name, { @@ -460,7 +460,7 @@ describe('splitInBatchesHandler', () => { workflowId: 'test-workflow', workflowName: 'Test Workflow', settings: {}, - addNodeWithSubnodes: jest.fn((node: NodeInstance) => { + addNodeWithSubnodes: vi.fn((node: NodeInstance) => { nodes.set(node.name, { instance: node, connections: new Map>([ @@ -469,7 +469,7 @@ describe('splitInBatchesHandler', () => { }); return node.name; }), - addBranchToGraph: jest.fn((branch: unknown) => { + addBranchToGraph: vi.fn((branch: unknown) => { const branchNode = branch as NodeInstance; if (!nodes.has(branchNode.name)) { nodes.set(branchNode.name, { @@ -505,7 +505,7 @@ describe('splitInBatchesHandler', () => { workflowId: 'test-workflow', workflowName: 'Test Workflow', settings: {}, - addNodeWithSubnodes: jest.fn((node: NodeInstance) => { + addNodeWithSubnodes: vi.fn((node: NodeInstance) => { nodes.set(node.name, { instance: node, connections: new Map>([ @@ -514,7 +514,7 @@ describe('splitInBatchesHandler', () => { }); return node.name; }), - addBranchToGraph: jest.fn((branch: unknown) => { + addBranchToGraph: vi.fn((branch: unknown) => { const branchNode = branch as NodeInstance; if (!nodes.has(branchNode.name)) { nodes.set(branchNode.name, { diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/switch-case-handler.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/switch-case-handler.test.ts index 9c2029e698c..e3e77cfd73b 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/switch-case-handler.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/composite-handlers/switch-case-handler.test.ts @@ -49,14 +49,14 @@ function createMockContext(): MutablePluginContext { workflowId: 'test-workflow', workflowName: 'Test Workflow', settings: {}, - addNodeWithSubnodes: jest.fn((node: NodeInstance) => { + addNodeWithSubnodes: vi.fn((node: NodeInstance) => { nodes.set(node.name, { instance: node, connections: new Map(), }); return node.name; }), - addBranchToGraph: jest.fn((branch: unknown) => { + addBranchToGraph: vi.fn((branch: unknown) => { const branchNode = branch as NodeInstance; nodes.set(branchNode.name, { instance: branchNode, diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/registry.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/registry.test.ts index bf109cadb1a..7019e29e271 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/registry.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/registry.test.ts @@ -8,7 +8,7 @@ function createMockValidator(id: string, nodeTypes: string[] = [], priority = 0) name: `Mock Validator ${id}`, nodeTypes, priority, - validateNode: jest.fn().mockReturnValue([]), + validateNode: vi.fn().mockReturnValue([]), }; } @@ -23,7 +23,7 @@ function createMockCompositeHandler( name: `Mock Handler ${id}`, priority, canHandle: canHandleFn as (input: unknown) => input is unknown, - addNodes: jest.fn().mockReturnValue('mock-node'), + addNodes: vi.fn().mockReturnValue('mock-node'), }; } @@ -33,7 +33,7 @@ function createMockSerializer(id: string, format: string): SerializerPlugin { id, name: `Mock Serializer ${id}`, format, - serialize: jest.fn().mockReturnValue({}), + serialize: vi.fn().mockReturnValue({}), }; } @@ -218,12 +218,9 @@ describe('pluginRegistry singleton', () => { expect(pluginRegistry).toBeInstanceOf(PluginRegistry); }); - it('returns the same instance on multiple imports', () => { - // Import again to verify singleton - intentionally using require() to test module-level singleton - // eslint-disable-next-line @typescript-eslint/no-require-imports -- Testing CommonJS singleton behavior - const { pluginRegistry: anotherRegistry } = require('./registry') as { - pluginRegistry: PluginRegistry; - }; + it('returns the same instance on multiple imports', async () => { + // Import again to verify the module-level singleton is shared across imports + const { pluginRegistry: anotherRegistry } = await import('./registry'); expect(anotherRegistry).toBe(pluginRegistry); }); }); diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/string-utils.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/string-utils.test.ts index 65d91764955..5fe1b3ba969 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/string-utils.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/string-utils.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { JS_METHODS, filterMethodsFromPath, diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/validation-helpers.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/validation-helpers.test.ts index a252c4d415c..bfd9000558f 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/validation-helpers.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/validation-helpers.test.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from '@jest/globals'; - import { containsExpression, containsMalformedExpression, diff --git a/packages/@n8n/workflow-sdk/tsconfig.json b/packages/@n8n/workflow-sdk/tsconfig.json index 761fb1a3474..fc8fe84ab48 100644 --- a/packages/@n8n/workflow-sdk/tsconfig.json +++ b/packages/@n8n/workflow-sdk/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@n8n/typescript-config/tsconfig.common.json", "compilerOptions": { - "types": ["node", "jest"], + "types": ["node", "vitest/globals"], "tsBuildInfoFile": "dist/typecheck.tsbuildinfo" }, "include": ["src/**/*.ts"], diff --git a/packages/@n8n/workflow-sdk/vitest.config.ts b/packages/@n8n/workflow-sdk/vitest.config.ts new file mode 100644 index 00000000000..9b9c0d4b8c7 --- /dev/null +++ b/packages/@n8n/workflow-sdk/vitest.config.ts @@ -0,0 +1,7 @@ +import { createVitestConfig } from '@n8n/vitest-config/node'; + +export default createVitestConfig({ + // The n8n root jest.config sets `restoreMocks: true`, and the test files rely on it. + restoreMocks: true, + globalSetup: ['./scripts/vitest-global-setup.ts'], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f44273b919..793853e8c13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2890,15 +2890,24 @@ importers: '@n8n/typescript-config': specifier: workspace:* version: link:../typescript-config + '@n8n/vitest-config': + specifier: workspace:* + version: link:../vitest-config '@types/adm-zip': specifier: ^0.5.7 version: 0.5.7 '@types/estree': specifier: ^1.0.8 version: 1.0.8 + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.1.1(vitest@4.1.1(@opentelemetry/api@1.9.0)(@types/node@20.19.41)(jsdom@23.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(vite@8.0.2(@types/node@20.19.41)(esbuild@0.25.10)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3))) adm-zip: specifier: ^0.5.16 version: 0.5.16 + vitest: + specifier: 'catalog:' + version: 4.1.1(@opentelemetry/api@1.9.0)(@types/node@20.19.41)(jsdom@23.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(vite@8.0.2(@types/node@20.19.41)(esbuild@0.25.10)(jiti@2.6.1)(sass-embedded@1.98.0)(sass@1.98.0)(terser@5.16.1)(tsx@4.19.3)(yaml@2.8.3)) packages/cli: dependencies: