mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
test: Add core entry points to allow easier test setup (#18597)
This commit is contained in:
@@ -1,10 +1,30 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import type { IWorkflowBase, ExecutionSummary } from 'n8n-workflow';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
||||
// Type for execution responses from the n8n API
|
||||
// Couldn't find the exact type so I put these ones together
|
||||
|
||||
interface ExecutionListResponse extends ExecutionSummary {
|
||||
data: string;
|
||||
workflowData: IWorkflowBase;
|
||||
}
|
||||
|
||||
import type { ApiHelpers } from './api-helper';
|
||||
import { TestError } from '../Types';
|
||||
import { resolveFromRoot } from '../utils/path-helper';
|
||||
|
||||
type WorkflowImportResult = {
|
||||
workflowId: string;
|
||||
createdWorkflow: IWorkflowBase;
|
||||
webhookPath?: string;
|
||||
webhookId?: string;
|
||||
};
|
||||
|
||||
export class WorkflowApiHelper {
|
||||
constructor(private api: ApiHelpers) {}
|
||||
|
||||
async createWorkflow(workflow: object) {
|
||||
async createWorkflow(workflow: IWorkflowBase) {
|
||||
const response = await this.api.request.post('/rest/workflows', { data: workflow });
|
||||
|
||||
if (!response.ok()) {
|
||||
@@ -27,7 +47,83 @@ export class WorkflowApiHelper {
|
||||
}
|
||||
}
|
||||
|
||||
async getExecutions(workflowId?: string, limit = 20) {
|
||||
/**
|
||||
* Make workflow unique by updating name, IDs, and webhook paths if present.
|
||||
* This ensures no conflicts when importing workflows for testing.
|
||||
*/
|
||||
private makeWorkflowUnique(
|
||||
workflow: IWorkflowBase,
|
||||
options?: { webhookPrefix?: string; idLength?: number },
|
||||
) {
|
||||
const idLength = options?.idLength ?? 12;
|
||||
const webhookPrefix = options?.webhookPrefix ?? 'test-webhook';
|
||||
const uniqueSuffix = nanoid(idLength);
|
||||
|
||||
// Make workflow name unique
|
||||
if (workflow.name) {
|
||||
workflow.name = `${workflow.name} (Test ${uniqueSuffix})`;
|
||||
}
|
||||
|
||||
// Check if workflow has webhook nodes and process them
|
||||
let webhookId: string | undefined;
|
||||
let webhookPath: string | undefined;
|
||||
|
||||
for (const node of workflow.nodes) {
|
||||
if (node.type === 'n8n-nodes-base.webhook') {
|
||||
webhookId = nanoid(idLength);
|
||||
webhookPath = `${webhookPrefix}-${webhookId}`;
|
||||
node.webhookId = webhookId;
|
||||
node.parameters.path = webhookPath;
|
||||
}
|
||||
}
|
||||
|
||||
return { webhookId, webhookPath, workflow };
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a workflow from an in-memory definition, making it unique for testing.
|
||||
* Returns detailed information about what was created.
|
||||
*/
|
||||
async createWorkflowFromDefinition(
|
||||
workflow: IWorkflowBase,
|
||||
options?: { webhookPrefix?: string; idLength?: number },
|
||||
): Promise<WorkflowImportResult> {
|
||||
const { webhookPath, webhookId } = this.makeWorkflowUnique(workflow, options);
|
||||
const createdWorkflow = await this.createWorkflow(workflow);
|
||||
const workflowId: string = String(createdWorkflow.id);
|
||||
|
||||
return {
|
||||
workflowId,
|
||||
createdWorkflow,
|
||||
webhookPath,
|
||||
webhookId,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a workflow from file and make it unique for testing.
|
||||
* The workflow will be created with its original active state from the JSON file.
|
||||
* Returns detailed information about what was imported, including webhook info if present.
|
||||
*/
|
||||
async importWorkflow(
|
||||
fileName: string,
|
||||
options?: { webhookPrefix?: string; idLength?: number },
|
||||
): Promise<WorkflowImportResult> {
|
||||
const workflowDefinition: IWorkflowBase = JSON.parse(
|
||||
readFileSync(resolveFromRoot('workflows', fileName), 'utf8'),
|
||||
);
|
||||
|
||||
const result = await this.createWorkflowFromDefinition(workflowDefinition, options);
|
||||
|
||||
// Ensure the workflow is in the correct active state as specified in the JSON
|
||||
if (workflowDefinition.active) {
|
||||
await this.setActive(result.workflowId, workflowDefinition.active);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async getExecutions(workflowId?: string, limit = 20): Promise<ExecutionListResponse[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (workflowId) params.set('workflowId', workflowId);
|
||||
params.set('limit', limit.toString());
|
||||
@@ -47,7 +143,7 @@ export class WorkflowApiHelper {
|
||||
return [];
|
||||
}
|
||||
|
||||
async getExecution(executionId: string) {
|
||||
async getExecution(executionId: string): Promise<ExecutionListResponse> {
|
||||
const response = await this.api.request.get(`/rest/executions/${executionId}`);
|
||||
|
||||
if (!response.ok()) {
|
||||
@@ -58,7 +154,7 @@ export class WorkflowApiHelper {
|
||||
return result.data ?? result;
|
||||
}
|
||||
|
||||
async waitForExecution(workflowId: string, timeoutMs = 10000) {
|
||||
async waitForExecution(workflowId: string, timeoutMs = 10000): Promise<ExecutionListResponse> {
|
||||
const initialExecutions = await this.getExecutions(workflowId, 50);
|
||||
const initialCount = initialExecutions.length;
|
||||
const startTime = Date.now();
|
||||
@@ -77,7 +173,9 @@ export class WorkflowApiHelper {
|
||||
for (const execution of executions) {
|
||||
const isCompleted = execution.status === 'success' || execution.status === 'error';
|
||||
if (isCompleted && execution.mode === 'webhook') {
|
||||
const executionTime = new Date(execution.startedAt ?? execution.createdAt).getTime();
|
||||
const executionTime = new Date(
|
||||
execution.startedAt ?? execution.createdAt ?? Date.now(),
|
||||
).getTime();
|
||||
if (executionTime >= startTime - 5000) {
|
||||
return execution;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user