import type { Logger } from '@n8n/backend-common'; import { mockInstance } from '@n8n/backend-test-utils'; import { WorkflowEntity } from '@n8n/db'; import { Container } from '@n8n/di'; import { mock } from 'jest-mock-extended'; import { BinaryDataConfig, BinaryDataService, InstanceSettings, UnrecognizedNodeTypeError, type DirectoryLoader, type ErrorReporter, } from 'n8n-core'; import { Ftp } from 'n8n-nodes-base/credentials/Ftp.credentials'; import { GithubApi } from 'n8n-nodes-base/credentials/GithubApi.credentials'; import { HttpBasicAuth } from 'n8n-nodes-base/credentials/HttpBasicAuth.credentials'; import { HttpHeaderAuth } from 'n8n-nodes-base/credentials/HttpHeaderAuth.credentials'; import { OpenAiApi } from 'n8n-nodes-base/credentials/OpenAiApi.credentials'; import { Cron } from 'n8n-nodes-base/nodes/Cron/Cron.node'; import { FormTrigger } from 'n8n-nodes-base/nodes/Form/FormTrigger.node'; import { ManualTrigger } from 'n8n-nodes-base/nodes/ManualTrigger/ManualTrigger.node'; import { ScheduleTrigger } from 'n8n-nodes-base/nodes/Schedule/ScheduleTrigger.node'; import { Set } from 'n8n-nodes-base/nodes/Set/Set.node'; import { Webhook as WebhookNode } from 'n8n-nodes-base/nodes/Webhook/Webhook.node'; import type { INodeType, INodeTypeData, INode } from 'n8n-workflow'; import type request from 'supertest'; import { v4 as uuid } from 'uuid'; import { AUTH_COOKIE_NAME } from '@/constants'; import { ExecutionService } from '@/executions/execution.service'; import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials'; import { Push } from '@/push'; export { setupTestServer } from './test-server'; // ---------------------------------- // initializers // ---------------------------------- /** * Initialize node types. */ export async function initActiveWorkflowManager() { mockInstance(BinaryDataConfig); mockInstance(InstanceSettings, { isMultiMain: false, n8nFolder: '/tmp/n8n-test', }); mockInstance(Push); mockInstance(ExecutionService); const { ActiveWorkflowManager } = await import('@/active-workflow-manager'); const activeWorkflowManager = Container.get(ActiveWorkflowManager); await activeWorkflowManager.init(); return activeWorkflowManager; } /** * Initialize node types. */ export async function initCredentialsTypes(): Promise { Container.get(LoadNodesAndCredentials).loaded.credentials = { githubApi: { type: new GithubApi(), sourcePath: '', }, ftp: { type: new Ftp(), sourcePath: '', }, openAiApi: { type: new OpenAiApi(), sourcePath: '', }, httpHeaderAuth: { type: new HttpHeaderAuth(), sourcePath: '', }, httpBasicAuth: { type: new HttpBasicAuth(), sourcePath: '', }, }; } /** * Initialize node types. */ export async function initNodeTypes(customNodes?: INodeTypeData) { const defaultNodes: INodeTypeData = { 'n8n-nodes-base.manualTrigger': { type: new ManualTrigger(), sourcePath: '', }, 'n8n-nodes-base.cron': { type: new Cron(), sourcePath: '', }, 'n8n-nodes-base.set': { type: new Set(), sourcePath: '', }, 'n8n-nodes-base.scheduleTrigger': { type: new ScheduleTrigger(), sourcePath: '', }, 'n8n-nodes-base.formTrigger': { type: new FormTrigger(), sourcePath: '', }, 'n8n-nodes-base.webhook': { type: mock({ description: new WebhookNode().description }), sourcePath: '', }, }; ScheduleTrigger.prototype.trigger = async () => ({}); const nodes = customNodes ?? defaultNodes; const loader = mock(); loader.getNode.mockImplementation((nodeType) => { const node = nodes[`n8n-nodes-base.${nodeType}`]; if (!node) throw new UnrecognizedNodeTypeError('n8n-nodes-base', nodeType); return node; }); const loadNodesAndCredentials = Container.get(LoadNodesAndCredentials); loadNodesAndCredentials.loaders = { 'n8n-nodes-base': loader }; loadNodesAndCredentials.loaded.nodes = nodes; } /** * Initialize a BinaryDataService for test runs. */ export async function initBinaryDataService(mode: 'default' | 'filesystem' = 'default') { const config = mock({ mode, availableModes: [mode], localStoragePath: '', }); const logger = mock(); const errorReporter = mock(); const binaryDataService = new BinaryDataService(config, errorReporter, logger); await binaryDataService.init(); Container.set(BinaryDataService, binaryDataService); } /** * Extract the value (token) of the auth cookie in a response. */ export function getAuthToken(response: request.Response, authCookieName = AUTH_COOKIE_NAME) { const cookiesHeader = response.headers['set-cookie']; if (!cookiesHeader) return undefined; const cookies = Array.isArray(cookiesHeader) ? cookiesHeader : [cookiesHeader]; const authCookie = cookies.find((c) => c.startsWith(`${authCookieName}=`)); if (!authCookie) return undefined; const match = authCookie.match(new RegExp(`(^| )${authCookieName}=(?[^;]+)`)); if (!match?.groups) return undefined; return match.groups.token; } // ---------------------------------- // community nodes // ---------------------------------- export * from './community-nodes'; // ---------------------------------- // workflow // ---------------------------------- export function makeWorkflow(options?: { withPinData: boolean; withCredential?: { id: string; name: string }; }) { const workflow = new WorkflowEntity(); const node: INode = { id: uuid(), name: 'Cron', type: 'n8n-nodes-base.cron', parameters: {}, typeVersion: 1, position: [740, 240], }; if (options?.withCredential) { node.credentials = { spotifyApi: options.withCredential, }; } workflow.name = 'My Workflow'; workflow.active = false; workflow.activeVersionId = null; workflow.connections = {}; workflow.nodes = [node]; if (options?.withPinData) { workflow.pinData = MOCK_PINDATA; } return workflow; } export const MOCK_PINDATA = { Spotify: [{ json: { myKey: 'myValue' } }] };