import { IConnections , INode , INodeExecutionData , INodeParameters , IRunExecutionData , Workflow , } from '../src' ; process . env . TEST_VARIABLE_1 = 'valueEnvVariable1' ; import * as Helpers from './Helpers' ; interface StubNode { name : string ; parameters : INodeParameters ; } describe ( 'Workflow' , ( ) => { describe ( 'renameNodeInExpressions' , ( ) => { const tests = [ { description : 'do nothing if there is no expression' , input : { currentName : 'Node1' , newName : 'Node1New' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , output : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { description : 'should work with dot notation' , input : { currentName : 'Node1' , newName : 'NewName' , parameters : { value1 : "={{$node.Node1.data.value1 + 'Node1'}}" , value2 : "={{$node.Node1.data.value2 + ' - ' + $node.Node1.data.value2}}" , } , } , output : { value1 : "={{$node.NewName.data.value1 + 'Node1'}}" , value2 : "={{$node.NewName.data.value2 + ' - ' + $node.NewName.data.value2}}" , } , } , { description : 'should work with ["nodeName"]' , input : { currentName : 'Node1' , newName : 'NewName' , parameters : { value1 : '={{$node["Node1"]["data"]["value1"] + \'Node1\'}}' , value2 : '={{$node["Node1"]["data"]["value2"] + \' - \' + $node["Node1"]["data"]["value2"]}}' , } , } , output : { value1 : '={{$node["NewName"]["data"]["value1"] + \'Node1\'}}' , value2 : '={{$node["NewName"]["data"]["value2"] + \' - \' + $node["NewName"]["data"]["value2"]}}' , } , } , { description : "should work with ['nodeName']" , input : { currentName : 'Node1' , newName : 'NewName' , parameters : { value1 : "={{$node['Node1']['data']['value1'] + 'Node1'}}" , value2 : "={{$node['Node1']['data']['value2'] + ' - ' + $node['Node1']['data']['value2']}}" , } , } , output : { value1 : "={{$node['NewName']['data']['value1'] + 'Node1'}}" , value2 : "={{$node['NewName']['data']['value2'] + ' - ' + $node['NewName']['data']['value2']}}" , } , } , { description : 'should work on lower levels' , input : { currentName : 'Node1' , newName : 'NewName' , parameters : { level1a : "={{$node.Node1.data.value1 + 'Node1'}}" , level1b : [ { value2a : "={{$node.Node1.data.value1 + 'Node1'}}" , value2b : "={{$node.Node1.data.value1 + 'Node1'}}" , } , ] , level1c : { value2a : { value3a : "={{$node.Node1.data.value1 + 'Node1'}}" , value3b : [ { value4a : "={{$node.Node1.data.value1 + 'Node1'}}" , value4b : { value5a : "={{$node.Node1.data.value1 + 'Node1'}}" , value5b : "={{$node.Node1.data.value1 + 'Node1'}}" , } , } , ] , } , } , } as INodeParameters , } , output : { level1a : "={{$node.NewName.data.value1 + 'Node1'}}" , level1b : [ { value2a : "={{$node.NewName.data.value1 + 'Node1'}}" , value2b : "={{$node.NewName.data.value1 + 'Node1'}}" , } , ] , level1c : { value2a : { value3a : "={{$node.NewName.data.value1 + 'Node1'}}" , value3b : [ { value4a : "={{$node.NewName.data.value1 + 'Node1'}}" , value4b : { value5a : "={{$node.NewName.data.value1 + 'Node1'}}" , value5b : "={{$node.NewName.data.value1 + 'Node1'}}" , } , } , ] , } , } , } , } , ] ; const nodeTypes = Helpers . NodeTypes ( ) ; const workflow = new Workflow ( { nodes : [ ] , connections : { } , active : false , nodeTypes } ) ; for ( const testData of tests ) { test ( testData . description , ( ) => { const result = workflow . renameNodeInExpressions ( testData . input . parameters , testData . input . currentName , testData . input . newName ) ; expect ( result ) . toEqual ( testData . output ) ; } ) ; } } ) ; describe ( 'renameNode' , ( ) => { const tests = [ { description : 'rename node without connections' , input : { currentName : 'Node1' , newName : 'Node1New' , nodes : [ { name : 'Node1' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , ] , connections : { } , } , output : { nodes : [ { name : 'Node1New' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , ] , connections : { } , } , } , { description : 'rename node with one output connection' , input : { currentName : 'Node1' , newName : 'Node1New' , nodes : [ { name : 'Node1' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2' , parameters : { value1 : 'value1Node2' , value2 : 'value2Node2' , } , } , ] , connections : { Node1 : { main : [ [ { node : 'Node2' , type : 'main' , index : 0 , } , ] , ] , } , } , } , output : { nodes : [ { name : 'Node1New' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2' , parameters : { value1 : 'value1Node2' , value2 : 'value2Node2' , } , } , ] , connections : { Node1New : { main : [ [ { node : 'Node2' , type : 'main' , index : 0 , } , ] , ] , } , } , } , } , { description : 'rename node with one input connection' , input : { currentName : 'Node2' , newName : 'Node2New' , nodes : [ { name : 'Node1' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2' , parameters : { value1 : 'value1Node2' , value2 : 'value2Node2' , } , } , ] , connections : { Node1 : { main : [ [ { node : 'Node2' , type : 'main' , index : 0 , } , ] , ] , } , } , } , output : { nodes : [ { name : 'Node1' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2New' , parameters : { value1 : 'value1Node2' , value2 : 'value2Node2' , } , } , ] , connections : { Node1 : { main : [ [ { node : 'Node2New' , type : 'main' , index : 0 , } , ] , ] , } , } , } , } , { description : 'rename node with multiple input and output connection' , input : { currentName : 'Node3' , newName : 'Node3New' , nodes : [ { name : 'Node1' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2' , parameters : { value1 : 'value1Node2' , value2 : 'value2Node2' , } , } , { name : 'Node3' , parameters : { value1 : 'value1Node3' , value2 : 'value2Node3' , } , } , { name : 'Node4' , parameters : { value1 : 'value1Node4' , value2 : 'value2Node4' , } , } , { name : 'Node5' , parameters : { value1 : 'value1Node5' , value2 : 'value2Node5' , } , } , ] , connections : { Node1 : { main : [ [ { node : 'Node3' , type : 'main' , index : 0 , } , ] , ] , } , Node2 : { main : [ [ { node : 'Node3' , type : 'main' , index : 0 , } , { node : 'Node5' , type : 'main' , index : 0 , } , ] , ] , } , Node3 : { main : [ [ { node : 'Node4' , type : 'main' , index : 0 , } , { node : 'Node5' , type : 'main' , index : 0 , } , ] , ] , } , } , } , output : { nodes : [ { name : 'Node1' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2' , parameters : { value1 : 'value1Node2' , value2 : 'value2Node2' , } , } , { name : 'Node3New' , parameters : { value1 : 'value1Node3' , value2 : 'value2Node3' , } , } , { name : 'Node4' , parameters : { value1 : 'value1Node4' , value2 : 'value2Node4' , } , } , { name : 'Node5' , parameters : { value1 : 'value1Node5' , value2 : 'value2Node5' , } , } , ] , connections : { Node1 : { main : [ [ { node : 'Node3New' , type : 'main' , index : 0 , } , ] , ] , } , Node2 : { main : [ [ { node : 'Node3New' , type : 'main' , index : 0 , } , { node : 'Node5' , type : 'main' , index : 0 , } , ] , ] , } , Node3New : { main : [ [ { node : 'Node4' , type : 'main' , index : 0 , } , { node : 'Node5' , type : 'main' , index : 0 , } , ] , ] , } , } , } , } , { description : 'change name also in expressions which use node-name (dot notation)' , input : { currentName : 'Node1' , newName : 'Node1New' , nodes : [ { name : 'Node1' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2' , parameters : { value1 : "={{$node.Node1.data.value1 + 'Node1'}}" , value2 : "={{$node.Node1.data.value2 + ' - ' + $node.Node1.data.value2}}" , } , } , ] , connections : { } , } , output : { nodes : [ { name : 'Node1New' , parameters : { value1 : 'value1Node1' , value2 : 'value2Node1' , } , } , { name : 'Node2' , parameters : { value1 : "={{$node.Node1New.data.value1 + 'Node1'}}" , value2 : "={{$node.Node1New.data.value2 + ' - ' + $node.Node1New.data.value2}}" , } , } , ] , connections : { } , } , } , ] ; const nodeTypes = Helpers . NodeTypes ( ) ; let workflow ; function createNodeData ( stubData ) { return { name : stubData . name , parameters : stubData . parameters , type : 'test.set' , typeVersion : 1 , position : [ 100 , 100 ] , } ; } let executeNodes ; let resultNodes ; for ( const testData of tests ) { test ( testData . description , ( ) => { executeNodes = [ ] ; for ( const node of testData . input . nodes ) { executeNodes . push ( createNodeData ( node ) ) ; } workflow = new Workflow ( { nodes : executeNodes , connections : testData . input . connections as IConnections , active : false , nodeTypes , } ) ; workflow . renameNode ( testData . input . currentName , testData . input . newName ) ; resultNodes = { } ; for ( const node of testData . output . nodes ) { resultNodes [ node . name ] = createNodeData ( node ) ; } expect ( workflow . nodes ) . toEqual ( resultNodes ) ; expect ( workflow . connectionsBySourceNode ) . toEqual ( testData . output . connections ) ; } ) ; } } ) ; describe ( 'getParameterValue' , ( ) => { const tests = [ { description : 'read simple not expression value' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : 'valueNode2' , } , } , } , output : { value1 : 'valueNode2' , } , } , { description : 'read simple math expression' , input : { Node1 : { parameters : { value1 : '' , } , } , Node2 : { parameters : { value1 : '={{1+2}}' , } , } , } , output : { value1 : 3 , } , } , { description : 'read data from node-output-data with with long "$node.{NODE}.data" syntax' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$node.Node1.data.value1}}' , } , } , } , output : { value1 : 'valueNode1' , } , } , { description : 'read data from node-output-data with with long "$node.{NODE}.data" syntax add value and append text' , input : { Node1 : { parameters : { value1 : 1 , } , } , Node2 : { parameters : { value1 : '={{$node.Node1.data.value1 + 2}} asdf' , } , } , } , output : { value1 : '3 asdf' , } , } , { description : 'read deep-data from node-output-data with with long "$node.{NODE}.data" syntax with JavaScript Code' , input : { Node1 : { parameters : { value1 : 'whatever' , } , outputJson : { value1 : { a : 1 , b : 2 , c : 3 , } , } , } , Node2 : { parameters : { value1 : '={{Object.keys($node.Node1.data.value1).join(", ")}}' , } , } , } , output : { value1 : 'a, b, c' , } , } , { description : 'read data from incoming-node-data with with short "data" syntax' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$data.value1}}' , } , } , } , output : { value1 : 'valueNode1' , } , } , { description : 'read deep-data from incoming-node-data with with short "data" syntax' , input : { Node1 : { parameters : { value1 : 'whatever' , } , outputJson : { value1 : { a : { b : 'deepDataNode1' , } , } , } , } , Node2 : { parameters : { value1 : '={{$data.value1.a.b}}' , } , } , } , output : { value1 : 'deepDataNode1' , } , } , { description : 'read deep-data from node-output-data with with long "$node.{NODE}.data" syntax' , input : { Node1 : { parameters : { value1 : 'whatever' , } , outputJson : { value1 : { a : { b : 'deepDataNode1' , } , } , } , } , Node2 : { parameters : { value1 : '={{$node.Node1.data.value1.a.b}}' , } , } , } , output : { value1 : 'deepDataNode1' , } , } , { description : 'read binary-string-data from incoming-node-data with with short "$binary" syntax' , input : { Node1 : { parameters : { value1 : 'whatever' , } , outputBinary : { binaryKey : { data : '' , type : '' , fileName : 'test-file1.jpg' , } , } , } , Node2 : { parameters : { value1 : '={{$binary.binaryKey.fileName}}' , } , } , } , output : { value1 : 'test-file1.jpg' , } , } , { description : 'read binary-string-data from node-output-data with with long "$node.{NODE}.binary" syntax' , input : { Node1 : { parameters : { value1 : 'whatever' , } , outputBinary : { binaryKey : { data : '' , type : '' , fileName : 'test-file1.jpg' , } , } , } , Node2 : { parameters : { value1 : '={{$node.Node1.binary.binaryKey.fileName}}' , } , } , } , output : { value1 : 'test-file1.jpg' , } , } , { description : 'read parameter from other node with with long "$node.parameter" syntax' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$node.Node1.parameter.value1}}' , } , } , } , output : { value1 : 'valueNode1' , } , } , { description : 'read environment data "$env" syntax which exists' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$env.TEST_VARIABLE_1}}' , } , } , } , output : { value1 : 'valueEnvVariable1' , } , } , { description : 'read environment data "$env" syntax which does not exists' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$env.DOES_NOT_EXIST}}' , } , } , } , output : { value1 : undefined , } , } , { description : 'read parameter from current node with short "$parameter" syntax' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : 'valueNode2' , value2 : '={{$parameter.value1}}' , } , } , } , output : { value1 : 'valueNode2' , value2 : 'valueNode2' , } , } , { description : 'return resolved value when referencing another property with expression (long "$node.{NODE}.data" syntax)' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$node.Node1.data.value1}}' , value2 : '={{$parameter.value1}}' , } , } , } , output : { value1 : 'valueNode1' , value2 : 'valueNode1' , } , } , { description : 'return resolved value when referencing another property with expression (short "data" syntax)' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$data.value1}}' , value2 : '={{$parameter.value1}}' , } , } , } , output : { value1 : 'valueNode1' , value2 : 'valueNode1' , } , } , { description : 'return resolved value when referencing another property with expression when a node has spaces (long "$node["{NODE}"].parameter" syntax)' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$node["Node 4 with spaces"].parameter.value1}}' , } , } , } , output : { value1 : 'default-value1' , } , } , { description : 'return resolved value when referencing another property with expression on another node (long "$node["{NODE}"].parameter" syntax)' , input : { Node1 : { parameters : { value1 : 'valueNode1' , } , } , Node2 : { parameters : { value1 : '={{$node["Node1"].parameter.value1}}a' , } , } , Node3 : { parameters : { value1 : '={{$node["Node2"].parameter.value1}}b' , } , } , } , output : { value1 : 'valueNode1ab' , } , } , ] ; const nodeTypes = Helpers . NodeTypes ( ) ; for ( const testData of tests ) { test ( testData . description , ( ) => { const nodes = [ { name : 'Node1' , parameters : testData . input . Node1 . parameters , type : 'test.set' , typeVersion : 1 , position : [ 100 , 100 ] , } , { name : 'Node2' , parameters : testData . input . Node2 . parameters , type : 'test.set' , typeVersion : 1 , position : [ 100 , 200 ] , } , { name : 'Node3' , parameters : testData . input . hasOwnProperty ( 'Node3' ) ? testData . input . Node3 . parameters : { } , type : 'test.set' , typeVersion : 1 , position : [ 100 , 300 ] , } , { name : 'Node 4 with spaces' , parameters : testData . input . hasOwnProperty ( 'Node4' ) ? testData . input . Node4 . parameters : { } , type : 'test.set' , typeVersion : 1 , position : [ 100 , 400 ] , } , ] ; const connections = { Node1 : { main : [ [ { node : 'Node2' , type : 'main' , index : 0 , } , ] , ] , } , Node2 : { main : [ [ { node : 'Node3' , type : 'main' , index : 0 , } , ] , ] , } , } ; const workflow = new Workflow ( { nodes , connections , active : false , nodeTypes } ) ; const activeNodeName = testData . input . hasOwnProperty ( 'Node3' ) ? 'Node3' : 'Node2' ; const runExecutionData = { resultData : { runData : { Node1 : [ { startTime : 1 , executionTime : 1 , data : { main : [ [ { json : testData . input . Node1 . outputJson || testData . input . Node1 . parameters , binary : testData . input . Node1 . outputBinary , } , ] , ] , } , } , ] , } , } , } ; const itemIndex = 0 ; const runIndex = 0 ; const connectionInputData = runExecutionData . resultData . runData ! [ 'Node1' ] ! [ 0 ] ! . data ! . main [ 0 ] ! ; for ( const parameterName of Object . keys ( testData . output ) ) { const parameterValue = nodes . find ( ( node ) => node . name === activeNodeName ) ! . parameters [ parameterName ] ; const result = workflow . expression . getParameterValue ( parameterValue , runExecutionData , runIndex , itemIndex , activeNodeName , connectionInputData , 'manual' , { } ) ; expect ( result ) . toEqual ( testData . output [ parameterName ] ) ; } } ) ; } test ( 'should also resolve all child parameters when the parent get requested' , ( ) => { const nodeTypes = Helpers . NodeTypes ( ) ; const nodes = [ { name : 'Node1' , parameters : { values : { string : [ { name : 'name1' , value : 'value1' , } , { name : 'name2' , value : '={{$parameter.values.string[0].value}}A' , } , ] , } , } , type : 'test.setMulti' , typeVersion : 1 , position : [ 100 , 100 ] , } , ] ; const connections = { } ; const workflow = new Workflow ( { nodes , connections , active : false , nodeTypes } ) ; const activeNodeName = 'Node1' ; const runExecutionData = { resultData : { runData : { Node1 : [ { startTime : 1 , executionTime : 1 , data : { main : [ [ { json : { } , } , ] , ] , } , } , ] , } , } , } ; const itemIndex = 0 ; const runIndex = 0 ; const connectionInputData = runExecutionData . resultData . runData ! [ 'Node1' ] ! [ 0 ] ! . data ! . main [ 0 ] ! ; const parameterName = 'values' ; const parameterValue = nodes . find ( ( node ) => node . name === activeNodeName ) ! . parameters [ parameterName ] ; const result = workflow . expression . getParameterValue ( parameterValue , runExecutionData , runIndex , itemIndex , activeNodeName , connectionInputData , 'manual' , { } ) ; expect ( result ) . toEqual ( { string : [ { name : 'name1' , value : 'value1' , } , { name : 'name2' , value : 'value1A' , } , ] , } ) ; } ) ; } ) ; } ) ;