From 889b15d33e579d3a64378b32b7f4300ff24d6db0 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:30:32 +0200 Subject: [PATCH] test: Add tests for $getWorkflowStaticData (#12203) --- .../__tests__/js-task-runner.test.ts | 171 ++++++++++++++++++ .../src/js-task-runner/__tests__/test-data.ts | 5 +- 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts index 67b7443ef7..e5df5b64a3 100644 --- a/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts +++ b/packages/@n8n/task-runner/src/js-task-runner/__tests__/js-task-runner.test.ts @@ -396,6 +396,177 @@ describe('JsTaskRunner', () => { }); }); + describe("$getWorkflowStaticData('global')", () => { + it('should have the global workflow static data available in runOnceForAllItems', async () => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: 'return { val: $getWorkflowStaticData("global") }', + nodeMode: 'runOnceForAllItems', + }), + taskData: newDataRequestResponse(inputItems.map(wrapIntoJson), { + staticData: { + global: { key: 'value' }, + }, + }), + }); + + expect(outcome.result).toEqual([wrapIntoJson({ val: { key: 'value' } })]); + }); + + it('should have the global workflow static data available in runOnceForEachItem', async () => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: 'return { val: $getWorkflowStaticData("global") }', + nodeMode: 'runOnceForEachItem', + }), + taskData: newDataRequestResponse(inputItems.map(wrapIntoJson), { + staticData: { + global: { key: 'value' }, + }, + }), + }); + + expect(outcome.result).toEqual([ + withPairedItem(0, wrapIntoJson({ val: { key: 'value' } })), + ]); + }); + + test.each<[CodeExecutionMode]>([['runOnceForAllItems'], ['runOnceForEachItem']])( + "does not return static data if it hasn't been modified in %s", + async (mode) => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: ` + const staticData = $getWorkflowStaticData("global"); + return { val: staticData }; + `, + nodeMode: mode, + }), + taskData: newDataRequestResponse(inputItems.map(wrapIntoJson), { + staticData: { + global: { key: 'value' }, + }, + }), + }); + + expect(outcome.staticData).toBeUndefined(); + }, + ); + + test.each<[CodeExecutionMode]>([['runOnceForAllItems'], ['runOnceForEachItem']])( + 'returns the updated static data in %s', + async (mode) => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: ` + const staticData = $getWorkflowStaticData("global"); + staticData.newKey = 'newValue'; + return { val: staticData }; + `, + nodeMode: mode, + }), + taskData: newDataRequestResponse(inputItems.map(wrapIntoJson), { + staticData: { + global: { key: 'value' }, + 'node:OtherNode': { some: 'data' }, + }, + }), + }); + + expect(outcome.staticData).toEqual({ + global: { key: 'value', newKey: 'newValue' }, + 'node:OtherNode': { some: 'data' }, + }); + }, + ); + }); + + describe("$getWorkflowStaticData('node')", () => { + const createTaskDataWithNodeStaticData = (nodeStaticData: IDataObject) => { + const taskData = newDataRequestResponse(inputItems.map(wrapIntoJson)); + const taskDataKey = `node:${taskData.node.name}`; + taskData.workflow.staticData = { + global: { 'global-key': 'global-value' }, + 'node:OtherNode': { 'other-key': 'other-value' }, + [taskDataKey]: nodeStaticData, + }; + + return taskData; + }; + + it('should have the node workflow static data available in runOnceForAllItems', async () => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: 'return { val: $getWorkflowStaticData("node") }', + nodeMode: 'runOnceForAllItems', + }), + taskData: createTaskDataWithNodeStaticData({ key: 'value' }), + }); + + expect(outcome.result).toEqual([wrapIntoJson({ val: { key: 'value' } })]); + }); + + it('should have the node workflow static data available in runOnceForEachItem', async () => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: 'return { val: $getWorkflowStaticData("node") }', + nodeMode: 'runOnceForEachItem', + }), + taskData: createTaskDataWithNodeStaticData({ key: 'value' }), + }); + + expect(outcome.result).toEqual([ + withPairedItem(0, wrapIntoJson({ val: { key: 'value' } })), + ]); + }); + + test.each<[CodeExecutionMode]>([['runOnceForAllItems'], ['runOnceForEachItem']])( + "does not return static data if it hasn't been modified in %s", + async (mode) => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: ` + const staticData = $getWorkflowStaticData("node"); + return { val: staticData }; + `, + nodeMode: mode, + }), + taskData: createTaskDataWithNodeStaticData({ key: 'value' }), + }); + + expect(outcome.staticData).toBeUndefined(); + }, + ); + + test.each<[CodeExecutionMode]>([['runOnceForAllItems'], ['runOnceForEachItem']])( + 'returns the updated static data in %s', + async (mode) => { + const outcome = await execTaskWithParams({ + task: newTaskWithSettings({ + code: ` + const staticData = $getWorkflowStaticData("node"); + staticData.newKey = 'newValue'; + return { val: staticData }; + `, + nodeMode: mode, + }), + taskData: createTaskDataWithNodeStaticData({ key: 'value' }), + }); + + expect(outcome.staticData).toEqual({ + global: { 'global-key': 'global-value' }, + 'node:JsCode': { + key: 'value', + newKey: 'newValue', + }, + 'node:OtherNode': { + 'other-key': 'other-value', + }, + }); + }, + ); + }); + it('should allow access to Node.js Buffers', async () => { const outcomeAll = await execTaskWithParams({ task: newTaskWithSettings({ diff --git a/packages/@n8n/task-runner/src/js-task-runner/__tests__/test-data.ts b/packages/@n8n/task-runner/src/js-task-runner/__tests__/test-data.ts index ef910e838f..f13939e51e 100644 --- a/packages/@n8n/task-runner/src/js-task-runner/__tests__/test-data.ts +++ b/packages/@n8n/task-runner/src/js-task-runner/__tests__/test-data.ts @@ -50,7 +50,9 @@ export const newTaskData = (opts: Partial & Pick */ export const newDataRequestResponse = ( inputData: INodeExecutionData[], - opts: Partial = {}, + opts: Partial & { + staticData?: IDataObject; + } = {}, ): DataRequestResponse => { const codeNode = newNode({ name: 'JsCode', @@ -81,6 +83,7 @@ export const newDataRequestResponse = ( }, }, nodes: [manualTriggerNode, codeNode], + staticData: opts.staticData, }, inputData: { main: [inputData],